<?php

declare(strict_types=1);

use Builder\Core\Foundation\Concerns\HasPublishing;
use Builder\Core\Foundation\Enums\PublicationStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;

// ---------------------------------------------------------------------------
// Test double
// ---------------------------------------------------------------------------

final class HasPublishingTestModel extends Model
{
    use HasPublishing;

    protected $table = 'publishing_test_models';

    protected $guarded = [];
}

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

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

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

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

it('creates a record with draft status by default', function (): void {
    $model = HasPublishingTestModel::create([]);

    expect($model->status)->toBe(PublicationStatus::Draft)
        ->and($model->isDraft())->toBeTrue()
        ->and($model->isPublished())->toBeFalse()
        ->and($model->isScheduled())->toBeFalse();
});

it('publishes a record', function (): void {
    $model = HasPublishingTestModel::create([]);
    $model->publish();

    expect($model->fresh()->status)->toBe(PublicationStatus::Published)
        ->and($model->fresh()->isPublished())->toBeTrue()
        ->and($model->fresh()->publish_at)->toBeNull();
});

it('unpublishes a record', function (): void {
    $model = HasPublishingTestModel::create(['status' => 'published']);
    $model->unpublish();

    expect($model->fresh()->status)->toBe(PublicationStatus::Draft)
        ->and($model->fresh()->isDraft())->toBeTrue();
});

it('schedules a record for future publishing', function (): void {
    $model = HasPublishingTestModel::create([]);
    $publishAt = Carbon::parse('2030-01-15 09:00:00');
    $model->schedulePublish($publishAt);

    $fresh = $model->fresh();
    expect($fresh->status)->toBe(PublicationStatus::Scheduled)
        ->and($fresh->isScheduled())->toBeTrue()
        ->and($fresh->publish_at->toDateTimeString())->toBe('2030-01-15 09:00:00');
});

it('clears publish_at when status changes away from scheduled', function (): void {
    $model = HasPublishingTestModel::create([
        'status' => 'scheduled',
        'publish_at' => '2030-01-15 09:00:00',
    ]);

    $model->status = PublicationStatus::Draft;
    $model->save();

    expect($model->fresh()->publish_at)->toBeNull();
});

it('keeps publish_at when status remains scheduled', function (): void {
    $publishAt = Carbon::parse('2030-06-01 10:00:00');
    $model = HasPublishingTestModel::create([
        'status' => 'scheduled',
        'publish_at' => $publishAt,
    ]);

    $model->touch();

    expect($model->fresh()->publish_at->toDateTimeString())->toBe('2030-06-01 10:00:00');
});

it('scopes published records correctly', function (): void {
    HasPublishingTestModel::create(['status' => 'draft']);
    HasPublishingTestModel::create(['status' => 'published']);
    HasPublishingTestModel::create(['status' => 'scheduled', 'publish_at' => '2030-01-01']);

    expect(HasPublishingTestModel::published()->count())->toBe(1)
        ->and(HasPublishingTestModel::draft()->count())->toBe(1)
        ->and(HasPublishingTestModel::scheduled()->count())->toBe(1);
});

it('casts status to PublicationStatus enum', function (): void {
    $model = HasPublishingTestModel::create(['status' => 'published']);

    expect($model->fresh()->status)->toBe(PublicationStatus::Published)
        ->and($model->fresh()->status)->toBeInstanceOf(PublicationStatus::class);
});
