<?php

declare(strict_types=1);

use Builder\Core\Foundation\Concerns\HasTranslations;
use Builder\Core\Foundation\Contracts\Translatable;
use Builder\Core\Models\Language;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Schema;

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

final class HasTranslationsTestItem extends Model implements Translatable
{
    use HasTranslations;

    protected $table = 'test_translatable_items';

    protected $guarded = [];

    /** @var list<string> */
    protected array $translatable = ['title', 'slug'];
}

final class HasTranslationsTestItemTranslation extends Model
{
    protected $table = 'test_translatable_item_translations';

    protected $guarded = [];
}

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

beforeEach(function (): void {
    Schema::create('users', function ($table): void {
        $table->id();
        $table->string('name')->nullable();
        $table->string('email')->nullable()->unique();
        $table->string('password')->nullable();
        $table->json('user_widget_preferences')->nullable();
        $table->rememberToken();
        $table->timestamps();
    });

    Schema::create('test_translatable_items', function ($table): void {
        $table->id();
        $table->timestamps();
    });

    Schema::create('test_translatable_item_translations', function ($table): void {
        $table->id();
        $table->foreignId('test_translatable_item_id')->constrained('test_translatable_items')->cascadeOnDelete();
        $table->string('locale', 10);
        $table->string('title')->nullable();
        $table->string('slug')->nullable();
        $table->timestamps();
        $table->unique(['test_translatable_item_id', 'locale']);
    });
});

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

it('resolves translation model class by convention', function (): void {
    $item = new HasTranslationsTestItem;

    expect($item->translationModelClass())->toBe(HasTranslationsTestItemTranslation::class);
});

it('returns a HasMany relationship', function (): void {
    $item = HasTranslationsTestItem::create([]);

    expect($item->translations())->toBeInstanceOf(HasMany::class);
});

it('returns null when no translation exists for a locale', function (): void {
    $item = HasTranslationsTestItem::create([]);

    expect($item->getTranslation('en'))->toBeNull();
});

it('sets and gets a translation via Eloquent model', function (): void {
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello']);

    $translation = $item->getTranslation('en');

    expect($translation)->toBeInstanceOf(Model::class)
        ->and($translation->title)->toBe('Hello')
        ->and($translation->slug)->toBe('hello');
});

it('updates an existing translation on second setTranslation call', function (): void {
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello']);
    $item->setTranslation('en', ['title' => 'Updated', 'slug' => 'updated']);

    $translation = $item->getTranslation('en');

    expect($translation->title)->toBe('Updated');
    expect(HasTranslationsTestItemTranslation::where('test_translatable_item_id', $item->id)->count())->toBe(1);
});

it('returns all translations keyed by locale', function (): void {
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello']);
    $item->setTranslation('nl', ['title' => 'Hallo', 'slug' => 'hallo']);

    $translations = $item->getTranslations();

    expect($translations)->toHaveCount(2)
        ->and($translations->has('en'))->toBeTrue()
        ->and($translations->has('nl'))->toBeTrue();
});

it('hasTranslation returns false when none exists', function (): void {
    $item = HasTranslationsTestItem::create([]);

    expect($item->hasTranslation('en'))->toBeFalse();
});

it('hasTranslation returns true after setTranslation', function (): void {
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello']);

    expect($item->hasTranslation('en'))->toBeTrue();
});

it('getAttribute returns translatable value for current locale', function (): void {
    app()->setLocale('en');
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'English', 'slug' => 'english']);

    expect($item->title)->toBe('English');
});

it('toArray includes translatable attributes for current locale', function (): void {
    app()->setLocale('en');
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'English', 'slug' => 'english']);

    $array = $item->fresh()->toArray();

    expect($array)->toHaveKey('title', 'English')
        ->and($array)->toHaveKey('slug', 'english');
});

it('hreflangData returns empty array when Language table does not exist', function (): void {
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello-en']);

    $result = $item->hreflangData();

    expect($result)->toBeArray();
});

it('hreflangData returns slugs for active locales with x-default for default language', function (): void {
    Schema::dropIfExists('languages');
    Schema::create('languages', function ($table): void {
        $table->id();
        $table->string('code')->unique();
        $table->string('name');
        $table->boolean('is_active')->default(true);
        $table->boolean('is_default')->default(false);
        $table->unsignedInteger('order')->default(0);
        $table->timestamps();
    });

    Language::create(['code' => 'en', 'name' => 'English', 'is_active' => true, 'is_default' => true, 'order' => 0]);
    Language::create(['code' => 'nl', 'name' => 'Nederlands', 'is_active' => true, 'is_default' => false, 'order' => 1]);
    Language::create(['code' => 'fr', 'name' => 'Français', 'is_active' => false, 'is_default' => false, 'order' => 2]);

    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello']);
    $item->setTranslation('nl', ['title' => 'Hallo', 'slug' => 'hallo']);

    $result = $item->fresh()->hreflangData();

    expect($result)->toHaveKey('en', 'hello')
        ->and($result)->toHaveKey('nl', 'hallo')
        ->and($result)->toHaveKey('x-default', 'hello')
        ->and($result)->not->toHaveKey('fr'); // inactive excluded
});

it('hreflangData skips active languages that have no translation (continue branch)', function (): void {
    Schema::dropIfExists('languages');
    Schema::create('languages', function ($table): void {
        $table->id();
        $table->string('code')->unique();
        $table->string('name');
        $table->boolean('is_active')->default(true);
        $table->boolean('is_default')->default(false);
        $table->unsignedInteger('order')->default(0);
        $table->timestamps();
    });

    Language::create(['code' => 'en', 'name' => 'English', 'is_active' => true, 'is_default' => true, 'order' => 0]);
    Language::create(['code' => 'nl', 'name' => 'Nederlands', 'is_active' => true, 'is_default' => false, 'order' => 1]);
    // German is active but has NO translation — triggers the continue on line 114
    Language::create(['code' => 'de', 'name' => 'Deutsch', 'is_active' => true, 'is_default' => false, 'order' => 2]);

    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello']);
    $item->setTranslation('nl', ['title' => 'Hallo', 'slug' => 'hallo']);
    // No German translation

    $result = $item->fresh()->hreflangData();

    expect($result)->toHaveKey('en')
        ->and($result)->toHaveKey('nl')
        ->and($result)->not->toHaveKey('de'); // skipped via continue
});

it('canonicalUrl returns null when no translation exists for locale', function (): void {
    $item = HasTranslationsTestItem::create([]);

    expect($item->canonicalUrl('en'))->toBeNull();
});

it('canonicalUrl returns null when slug is empty', function (): void {
    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => '']);

    expect($item->fresh()->canonicalUrl('en'))->toBeNull();
});

it('canonicalUrl returns full url for valid translation slug', function (): void {
    config(['builder.public_url' => 'https://example.com']);

    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'hello-world']);

    expect($item->fresh()->canonicalUrl('en'))->toBe('https://example.com/hello-world');
});

it('canonicalUrl falls back to app.url when builder.public_url is not set', function (): void {
    config(['builder.public_url' => null, 'app.url' => 'https://fallback.com']);

    $item = HasTranslationsTestItem::create([]);
    $item->setTranslation('en', ['title' => 'Hello', 'slug' => 'page']);

    expect($item->fresh()->canonicalUrl('en'))->toBe('https://fallback.com/page');
});

it('setAttribute returns self without storing value when key is translatable', function (): void {
    $item = HasTranslationsTestItem::create([]);

    // Direct assignment on a translatable attribute goes through setAttribute()
    $item->title = 'should-not-persist';

    // The translatable attribute should NOT be stored on the model's own attributes
    expect(array_key_exists('title', $item->getAttributes()))->toBeFalse();
});
