<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Models\Setting;
use App\Services\AppSettings;
use Database\Seeders\SettingSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class AppSettingsTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    public function the_seeder_declares_the_baseline_settings(): void
    {
        $this->seed(SettingSeeder::class);

        $this->assertSame('Aumni Techworks LLP', setting('company_name'));
        $this->assertSame('Aumni AMS', setting('application_name'));
        $this->assertSame(25, setting('pagination_per_page'));
    }

    #[Test]
    public function values_are_cast_to_their_declared_type(): void
    {
        $this->seed(SettingSeeder::class);

        $this->assertIsInt(setting('pagination_per_page'));
        $this->assertIsBool(setting('enable_warranty_emails'));
        $this->assertIsArray(setting('warranty_alert_days'));
        $this->assertSame([30, 15, 7], setting('warranty_alert_days'));
        $this->assertIsString(setting('asset_tag_prefix'));
    }

    #[Test]
    public function a_missing_key_falls_back_to_the_given_default(): void
    {
        $this->assertSame('fallback', setting('a_key_that_does_not_exist', 'fallback'));
    }

    #[Test]
    public function writing_a_setting_invalidates_the_cache(): void
    {
        $this->seed(SettingSeeder::class);

        $this->assertSame('AUM', setting('asset_tag_prefix'));

        settings()->set('asset_tag_prefix', 'ATL');

        $this->assertSame('ATL', setting('asset_tag_prefix'));
    }

    #[Test]
    public function boolean_and_json_values_round_trip_through_storage(): void
    {
        $this->seed(SettingSeeder::class);

        settings()->set('enable_warranty_emails', true);
        settings()->set('warranty_alert_days', [60, 30]);

        $this->assertTrue(setting('enable_warranty_emails'));
        $this->assertSame([60, 30], setting('warranty_alert_days'));
    }

    #[Test]
    public function an_unknown_key_cannot_be_written(): void
    {
        // Settings are declared in SettingSeeder so every key has a label, a
        // type and a group for the Settings screen. A typo must fail loudly
        // rather than create an orphan row nothing can render.
        $this->expectException(InvalidArgumentException::class);

        settings()->set('not_a_declared_setting', 'value');
    }

    #[Test]
    public function reseeding_refreshes_metadata_without_overwriting_chosen_values(): void
    {
        $this->seed(SettingSeeder::class);

        settings()->set('company_name', 'Aumni Techworks LLP (Pune)');

        // A later deploy re-runs the seeder.
        $this->seed(SettingSeeder::class);

        $this->assertSame('Aumni Techworks LLP (Pune)', setting('company_name'));
    }

    #[Test]
    public function settings_resolve_to_an_empty_set_when_the_table_is_absent(): void
    {
        // Console commands and the very first migrate run before the table
        // exists; callers must fall back to their defaults rather than fail.
        Schema::drop('settings');

        $accessor = new AppSettings;

        $this->assertSame([], $accessor->all());
        $this->assertSame('default', $accessor->get('company_name', 'default'));
    }

    #[Test]
    public function every_declared_setting_has_presentation_metadata(): void
    {
        $this->seed(SettingSeeder::class);

        foreach (Setting::all() as $setting) {
            $this->assertNotEmpty($setting->label, "Setting [{$setting->key}] has no label.");
            $this->assertNotEmpty($setting->group, "Setting [{$setting->key}] has no group.");
            $this->assertNotEmpty($setting->type, "Setting [{$setting->key}] has no type.");
        }
    }
}
