<?php

namespace Tests\Tenant;

use App\Facades\Feature;
use App\Facades\Settings;
use Carbon\Carbon;
use Illuminate\Support\Facades\Event;
use Mtc\MercuryDataModels\Setting;
use Mtc\MercuryDataModels\Events\SettingUpdated;
use Tests\TenantTestCase;

class SettingRepositoryTest extends TenantTestCase
{
    protected bool $useTestSettingRepository = false;

    public function testLoadSettingValue()
    {
        Setting::factory()->create([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo',
            'type' => 'string',
            'value' => 'baz',
        ]);

        $this->assertEquals('baz', Settings::get('foo'));
    }

    public function testDefaultValue()
    {
        $this->assertEquals('baz', Settings::get('lorem-ipsum', 'baz'));
    }

    public function testFeatureFlagsOnSettings()
    {
        Feature::setEnabled(tenant(), ['imagin-studio-placeholders']);
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo',
            'value' => true,
            'type' => 'boolean',
        ]);
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'faz',
            'feature' => 'imagin-studio-placeholders',
            'value' => true,
            'type' => 'boolean',
        ]);
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'baz',
            'feature' => 'valuation_adjustments',
            'value' => true,
            'type' => 'boolean',
        ]);

        $this->assertTrue(Settings::get('foo'));
        $this->assertTrue(Settings::get('faz'));
        $this->assertNull(Settings::get('baz'));

    }

    public function testSettingValueCasting()
    {
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo',
            'value' => 'baz',
            'type' => 'string',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo-baz',
            'value' => '1',
            'type' => 'int',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo-bar',
            'value' => '1',
            'type' => 'bool',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo-baz-bar',
            'value' => '1.2',
            'type' => 'float',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo-bar-baz',
            'value' => '2022-01-13',
            'type' => 'datetime',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'secret-foo',
            'value' => 'foo baz bar',
            'type' => 'secret',
        ]);

        $this->assertEquals('baz', Settings::get('foo'));

        $this->assertIsInt(Settings::get('foo-baz'));
        $this->assertEquals(1, Settings::get('foo-baz'));

        $this->assertIsBool(Settings::get('foo-bar'));
        $this->assertEquals(true, Settings::get('foo-bar'));

        $this->assertIsFloat(Settings::get('foo-baz-bar'));
        $this->assertEquals(1.2, Settings::get('foo-baz-bar'));

        $this->assertInstanceOf(Carbon::class, Settings::get('foo-bar-baz'));
        $this->assertEquals(Carbon::parse('2022-01-13'), Settings::get('foo-bar-baz'));

        $this->assertEquals('foo baz bar', Settings::get('secret-foo'));
    }

    public function testUpdateCreatesSettingWhenMissing()
    {
        // Internal markers (e.g. the AutoGrab last-processed partition) are written via
        // update() without ever being registered, so the first write must create the row
        // rather than throwing ModelNotFoundException.
        $this->assertFalse(Setting::query()->where('config_key', 'internal-marker')->exists());

        Settings::update('internal-marker', 'partition-2026-07-09');

        $this->assertDatabaseHas('settings', [
            'config_key' => 'internal-marker',
            'value' => 'partition-2026-07-09',
        ]);
        $this->assertEquals('partition-2026-07-09', Settings::get('internal-marker'));
    }

    public function testUpdatePreservesMetadataOfExistingSetting()
    {
        // Updating an existing row fires SettingUpdated, whose queued listener would make the
        // tenancy queue bootstrapper re-resolve the tenant from a `tenants` table that the
        // fake-tenant harness does not have. That path is exercised elsewhere; here we only
        // care that the repository does not clobber existing metadata.
        Event::fake([SettingUpdated::class]);

        Setting::factory()->create([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'foo',
            'type' => 'string',
            'value' => 'baz',
        ]);

        Settings::update('foo', 'updated');

        $setting = Setting::query()->where('config_key', 'foo')->firstOrFail();
        $this->assertEquals('updated', $setting->value);
        // The NOT NULL defaults used for fresh keys must not overwrite an existing row.
        $this->assertEquals('string', $setting->type);
        $this->assertEquals('dolor', $setting->name);
    }
}
