<?php

declare(strict_types=1);

use Builder\Core\Foundation\Concerns\HasPublishing;
use Builder\Core\Foundation\Concerns\HasTranslatablePublishing;
use Builder\Core\Foundation\Contracts\HasPublishableRecords;
use Builder\Core\Foundation\Contracts\Publishable;
use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Enums\PublicationStatus;
use Builder\Core\Foundation\Registry;
use Builder\Core\Foundation\Resources\Resource;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;

// ---------------------------------------------------------------------------
// Test doubles
// ---------------------------------------------------------------------------

final class PublishableCommandTestModel extends Model implements Publishable
{
    use HasPublishing;

    protected $table = 'publishable_command_test_models';

    protected $guarded = [];
}

final class PublishableCommandTestResource extends Resource implements HasPublishableRecords
{
    public static function getSlug(): string
    {
        return 'publishable-command-test-models';
    }

    public static function getLabel(): string
    {
        return 'Test Models';
    }

    public static function getModel(): string
    {
        return PublishableCommandTestModel::class;
    }

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make();
    }
}

// ---------------------------------------------------------------------------
// Schema setup
// ---------------------------------------------------------------------------

beforeEach(function (): void {
    Schema::create('publishable_command_test_models', function ($table): void {
        $table->id();
        HasPublishing::publishingColumns($table);
        $table->timestamps();
    });

    app(Registry::class)->resource(PublishableCommandTestResource::class);
});

afterEach(function (): void {
    Schema::dropIfExists('publishable_command_test_models');
});

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

it('publishes overdue scheduled records', function (): void {
    $overdue = PublishableCommandTestModel::create([
        'status' => 'scheduled',
        'publish_at' => Carbon::now()->subMinute(),
    ]);
    $future = PublishableCommandTestModel::create([
        'status' => 'scheduled',
        'publish_at' => Carbon::now()->addHour(),
    ]);
    $draft = PublishableCommandTestModel::create(['status' => 'draft']);

    $this->artisan('builder:publish')->assertSuccessful();

    expect($overdue->fresh()->status)->toBe(PublicationStatus::Published)
        ->and($future->fresh()->status)->toBe(PublicationStatus::Scheduled)
        ->and($draft->fresh()->status)->toBe(PublicationStatus::Draft);
});

it('does nothing when no records are due', function (): void {
    PublishableCommandTestModel::create([
        'status' => 'scheduled',
        'publish_at' => Carbon::now()->addDay(),
    ]);

    $this->artisan('builder:publish')->assertSuccessful();

    expect(PublishableCommandTestModel::published()->count())->toBe(0);
});

it('skips resources that are not publishable', function (): void {
    // Only PublishableCommandTestResource is publishable — other resources should be skipped silently.
    $this->artisan('builder:publish')->assertSuccessful();
});

// ---------------------------------------------------------------------------
// Translatable publishing path tests
// ---------------------------------------------------------------------------

final class TranslatableCmdParentTranslation extends Model implements Publishable
{
    use HasPublishing;

    protected $table = 'translatable_cmd_translations';

    protected $guarded = [];
}

final class TranslatableCmdParent extends Model
{
    use HasTranslatablePublishing;

    protected $table = 'translatable_cmd_parents';

    protected $guarded = [];
}

final class TranslatableCmdResource extends Resource implements HasPublishableRecords
{
    public static function getSlug(): string
    {
        return 'translatable-cmd-parents';
    }

    public static function getLabel(): string
    {
        return 'Translatable Models';
    }

    public static function getModel(): string
    {
        return TranslatableCmdParent::class;
    }

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make();
    }
}

final class NonExistentTranslationCmdParent extends Model
{
    use HasTranslatablePublishing;

    protected $table = 'non_existent_translation_cmd_parents';

    protected $guarded = [];

    public static function translationModelClass(): string
    {
        return 'NonExistentTranslationClassForCmd';
    }
}

final class NonExistentTranslationCmdResource extends Resource implements HasPublishableRecords
{
    public static function getSlug(): string
    {
        return 'non-existent-translation-cmd-parents';
    }

    public static function getLabel(): string
    {
        return 'Non Existent Translation Models';
    }

    public static function getModel(): string
    {
        return NonExistentTranslationCmdParent::class;
    }

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make();
    }
}

it('skips translatable resource when translation class does not exist', function (): void {
    app(Registry::class)->resource(NonExistentTranslationCmdResource::class);

    $this->artisan('builder:publish')->assertSuccessful();
});

it('does nothing when no translatable records are due', function (): void {
    Schema::create('translatable_cmd_parents', function ($table): void {
        $table->id();
        $table->timestamps();
    });
    Schema::create('translatable_cmd_translations', function ($table): void {
        $table->id();
        $table->foreignId('parent_id');
        $table->string('locale')->default('en');
        HasPublishing::publishingColumns($table);
        $table->timestamps();
    });

    app(Registry::class)->resource(TranslatableCmdResource::class);

    TranslatableCmdParentTranslation::create([
        'parent_id' => 1,
        'locale' => 'en',
        'status' => 'scheduled',
        'publish_at' => Carbon::now()->addDay(),
    ]);

    $this->artisan('builder:publish')->assertSuccessful();

    expect(TranslatableCmdParentTranslation::published()->count())->toBe(0);

    Schema::dropIfExists('translatable_cmd_translations');
    Schema::dropIfExists('translatable_cmd_parents');
});

it('publishes overdue scheduled translatable records', function (): void {
    Schema::create('translatable_cmd_parents', function ($table): void {
        $table->id();
        $table->timestamps();
    });
    Schema::create('translatable_cmd_translations', function ($table): void {
        $table->id();
        $table->foreignId('parent_id');
        $table->string('locale')->default('en');
        HasPublishing::publishingColumns($table);
        $table->timestamps();
    });

    app(Registry::class)->resource(TranslatableCmdResource::class);

    $overdue = TranslatableCmdParentTranslation::create([
        'parent_id' => 1,
        'locale' => 'en',
        'status' => 'scheduled',
        'publish_at' => Carbon::now()->subMinute(),
    ]);

    $this->artisan('builder:publish')->assertSuccessful();

    expect($overdue->fresh()->status)->toBe(PublicationStatus::Published);

    Schema::dropIfExists('translatable_cmd_translations');
    Schema::dropIfExists('translatable_cmd_parents');
});
