<?php

namespace Tests\Unit\Models;

use App\Enums\AccountStatusEnum;
use App\Enums\PlatformEnum;
use App\Models\PlatformAccount;
use App\Models\PlatformChannel;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PlatformAccountTest extends TestCase
{
    use RefreshDatabase;

    public function test_fillable_fields(): void
    {
        $fillableFields = ['platform', 'instance_url', 'username', 'password', 'settings', 'is_active', 'last_tested_at', 'status', 'consecutive_failures'];
        $account = new PlatformAccount;

        $this->assertEquals($fillableFields, $account->getFillable());
    }

    public function test_table_name(): void
    {
        $account = new PlatformAccount;

        $this->assertEquals('platform_accounts', $account->getTable());
    }

    public function test_casts_platform_to_enum(): void
    {
        $account = PlatformAccount::factory()->create(['platform' => PlatformEnum::LEMMY]);

        $this->assertInstanceOf(PlatformEnum::class, $account->platform);
        $this->assertEquals(PlatformEnum::LEMMY, $account->platform);
        $this->assertEquals('lemmy', $account->platform->value);
    }

    public function test_casts_settings_to_array(): void
    {
        $settings = ['key1' => 'value1', 'nested' => ['key2' => 'value2']];

        $account = PlatformAccount::factory()->create(['settings' => $settings]);

        $this->assertIsArray($account->settings);
        $this->assertEquals($settings, $account->settings);
    }

    public function test_casts_is_active_to_boolean(): void
    {
        $account = PlatformAccount::factory()->create(['is_active' => '1']);

        $this->assertIsBool($account->is_active);
        $this->assertTrue($account->is_active);

        $account->update(['is_active' => '0']);
        $account->refresh();

        $this->assertIsBool($account->is_active);
        $this->assertFalse($account->is_active);
    }

    public function test_casts_last_tested_at_to_datetime(): void
    {
        $timestamp = now()->subHours(2);
        $account = PlatformAccount::factory()->create(['last_tested_at' => $timestamp]);

        $this->assertInstanceOf(Carbon::class, $account->last_tested_at);
        $this->assertEquals($timestamp->format('Y-m-d H:i:s'), $account->last_tested_at->format('Y-m-d H:i:s'));
    }

    public function test_password_encryption_and_decryption(): void
    {
        $plainPassword = 'my-secret-password';

        $account = PlatformAccount::factory()->create(['password' => $plainPassword]);

        // Password should be decrypted when accessing
        $this->assertEquals($plainPassword, $account->password);

        // But encrypted in the database
        $this->assertNotEquals($plainPassword, $account->getAttributes()['password']);
        $this->assertNotNull($account->getAttributes()['password']);
    }

    public function test_password_with_specific_value(): void
    {
        $password = 'specific-test-password';
        $account = PlatformAccount::factory()->create(['password' => $password]);

        $this->assertEquals($password, $account->password);
        $this->assertNotEquals($password, $account->getAttributes()['password']);
    }

    public function test_password_encryption_is_different_each_time(): void
    {
        $password = 'same-password';
        $account1 = PlatformAccount::factory()->create(['password' => $password]);
        $account2 = PlatformAccount::factory()->create(['password' => $password]);

        $this->assertEquals($password, $account1->password);
        $this->assertEquals($password, $account2->password);
        $this->assertNotEquals($account1->getAttributes()['password'], $account2->getAttributes()['password']);
    }

    public function test_password_decryption_handles_corruption(): void
    {
        $account = PlatformAccount::factory()->create();
        $originalPassword = $account->password;

        // Since the password attribute has special handling, this test verifies the basic functionality
        $this->assertNotNull($originalPassword);
        $this->assertIsString($originalPassword);
    }

    public function test_get_active_static_method(): void
    {
        // Create active and inactive accounts
        $activeAccount1 = PlatformAccount::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'is_active' => true,
        ]);

        $activeAccount2 = PlatformAccount::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'is_active' => true,
        ]);

        $inactiveAccount = PlatformAccount::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'is_active' => false,
        ]);

        $activeAccounts = PlatformAccount::getActive(PlatformEnum::LEMMY);

        $this->assertCount(2, $activeAccounts);
        $this->assertTrue($activeAccounts->contains('id', $activeAccount1->id));
        $this->assertTrue($activeAccounts->contains('id', $activeAccount2->id));
        $this->assertFalse($activeAccounts->contains('id', $inactiveAccount->id));
    }

    public function test_set_as_active_method(): void
    {
        // Create multiple accounts for same platform
        $account1 = PlatformAccount::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'is_active' => true,
        ]);

        $account2 = PlatformAccount::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'is_active' => true,
        ]);

        $account3 = PlatformAccount::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'is_active' => false,
        ]);

        // Set account3 as active
        $account3->setAsActive();

        // Refresh all accounts
        $account1->refresh();
        $account2->refresh();
        $account3->refresh();

        // Only account3 should be active
        $this->assertFalse($account1->is_active);
        $this->assertFalse($account2->is_active);
        $this->assertTrue($account3->is_active);
    }

    public function test_belongs_to_many_channels_relationship(): void
    {
        $account = PlatformAccount::factory()->create();
        $channel1 = PlatformChannel::factory()->create();
        $channel2 = PlatformChannel::factory()->create();

        // Attach channels with pivot data
        $account->channels()->attach($channel1->id, [
            'is_active' => true,
            'priority' => 100,
        ]);

        $account->channels()->attach($channel2->id, [
            'is_active' => false,
            'priority' => 50,
        ]);

        $channels = $account->channels;

        $this->assertCount(2, $channels);
        $this->assertTrue($channels->contains('id', $channel1->id));
        $this->assertTrue($channels->contains('id', $channel2->id));

        // Test pivot data
        $channel1FromRelation = $channels->find($channel1->id);
        $this->assertEquals(1, $channel1FromRelation->pivot->is_active);
        $this->assertEquals(100, $channel1FromRelation->pivot->priority);

        $channel2FromRelation = $channels->find($channel2->id);
        $this->assertEquals(0, $channel2FromRelation->pivot->is_active);
        $this->assertEquals(50, $channel2FromRelation->pivot->priority);
    }

    public function test_active_channels_relationship(): void
    {
        $account = PlatformAccount::factory()->create();
        $activeChannel1 = PlatformChannel::factory()->create();
        $activeChannel2 = PlatformChannel::factory()->create();
        $inactiveChannel = PlatformChannel::factory()->create();

        // Attach channels
        $account->channels()->attach($activeChannel1->id, [
            'is_active' => true,
            'priority' => 100,
        ]);

        $account->channels()->attach($activeChannel2->id, [
            'is_active' => true,
            'priority' => 200,
        ]);

        $account->channels()->attach($inactiveChannel->id, [
            'is_active' => false,
            'priority' => 150,
        ]);

        $activeChannels = $account->activeChannels;

        $this->assertCount(2, $activeChannels);
        $this->assertTrue($activeChannels->contains('id', $activeChannel1->id));
        $this->assertTrue($activeChannels->contains('id', $activeChannel2->id));
        $this->assertFalse($activeChannels->contains('id', $inactiveChannel->id));

        // Test ordering by priority descending
        $channelIds = $activeChannels->pluck('id')->toArray();
        $this->assertEquals($activeChannel2->id, $channelIds[0]); // Priority 200
        $this->assertEquals($activeChannel1->id, $channelIds[1]); // Priority 100
    }

    public function test_account_creation_with_factory(): void
    {
        $account = PlatformAccount::factory()->create();

        $this->assertInstanceOf(PlatformAccount::class, $account);
        $this->assertInstanceOf(PlatformEnum::class, $account->platform);
        $this->assertEquals(PlatformEnum::LEMMY, $account->platform);
        $this->assertIsString($account->instance_url);
        $this->assertIsString($account->username);
        $this->assertEquals('test-password', $account->password);
        $this->assertIsBool($account->is_active);
        $this->assertTrue($account->is_active);
        $this->assertSame(AccountStatusEnum::UNTESTED, $account->status);
        $this->assertIsArray($account->settings);
    }

    public function test_account_creation_with_explicit_values(): void
    {
        $settings = ['custom' => 'value', 'nested' => ['key' => 'value']];
        $timestamp = now()->subHours(1);

        $account = PlatformAccount::create([
            'platform' => PlatformEnum::LEMMY,
            'instance_url' => 'https://lemmy.example.com',
            'username' => 'testuser',
            'password' => 'secret123',
            'settings' => $settings,
            'is_active' => false,
            'last_tested_at' => $timestamp,
            'status' => AccountStatusEnum::HEALTHY,
        ]);

        $this->assertEquals(PlatformEnum::LEMMY, $account->platform);
        $this->assertEquals('https://lemmy.example.com', $account->instance_url);
        $this->assertEquals('testuser', $account->username);
        $this->assertEquals('secret123', $account->password);
        $this->assertEquals($settings, $account->settings);
        $this->assertFalse($account->is_active);
        $this->assertEquals($timestamp->format('Y-m-d H:i:s'), $account->last_tested_at->format('Y-m-d H:i:s'));
        $this->assertSame(AccountStatusEnum::HEALTHY, $account->status);
    }

    public function test_account_factory_states(): void
    {
        $inactiveAccount = PlatformAccount::factory()->inactive()->create();
        $this->assertFalse($inactiveAccount->is_active);

        $testedAccount = PlatformAccount::factory()->tested()->create();
        $this->assertNotNull($testedAccount->last_tested_at);
        $this->assertSame(AccountStatusEnum::HEALTHY, $testedAccount->status);

        $failedAccount = PlatformAccount::factory()->failed()->create();
        $this->assertNotNull($failedAccount->last_tested_at);
        $this->assertSame(AccountStatusEnum::UNHEALTHY, $failedAccount->status);
    }

    public function test_account_update(): void
    {
        $account = PlatformAccount::factory()->create([
            'username' => 'original_user',
            'is_active' => true,
        ]);

        $account->update([
            'username' => 'updated_user',
            'is_active' => false,
        ]);

        $account->refresh();

        $this->assertEquals('updated_user', $account->username);
        $this->assertFalse($account->is_active);
    }

    public function test_account_deletion(): void
    {
        $account = PlatformAccount::factory()->create();
        $accountId = $account->id;

        $account->delete();

        $this->assertDatabaseMissing('platform_accounts', ['id' => $accountId]);
    }

    public function test_account_settings_can_be_empty_array(): void
    {
        $account = PlatformAccount::factory()->create(['settings' => []]);

        $this->assertIsArray($account->settings);
        $this->assertEmpty($account->settings);
    }

    public function test_account_settings_can_be_complex_structure(): void
    {
        $complexSettings = [
            'authentication' => [
                'method' => 'jwt',
                'timeout' => 30,
            ],
            'features' => ['posting', 'commenting'],
            'rate_limits' => [
                'posts_per_hour' => 10,
                'comments_per_hour' => 50,
            ],
        ];

        $account = PlatformAccount::factory()->create(['settings' => $complexSettings]);

        $this->assertEquals($complexSettings, $account->settings);
        $this->assertEquals('jwt', $account->settings['authentication']['method']);
        $this->assertEquals(['posting', 'commenting'], $account->settings['features']);
    }

    public function test_account_can_have_null_last_tested_at(): void
    {
        $account = PlatformAccount::factory()->create(['last_tested_at' => null]);

        $this->assertNull($account->last_tested_at);
    }

    public function test_account_timestamps(): void
    {
        $account = PlatformAccount::factory()->create();

        $this->assertNotNull($account->created_at);
        $this->assertNotNull($account->updated_at);
        $this->assertInstanceOf(Carbon::class, $account->created_at);
        $this->assertInstanceOf(Carbon::class, $account->updated_at);
    }

    public function test_account_can_have_multiple_channels_with_different_priorities(): void
    {
        $account = PlatformAccount::factory()->create();
        $channel1 = PlatformChannel::factory()->create();
        $channel2 = PlatformChannel::factory()->create();
        $channel3 = PlatformChannel::factory()->create();

        // Attach channels with different priorities
        $account->channels()->attach([
            $channel1->id => ['is_active' => true, 'priority' => 300],
            $channel2->id => ['is_active' => true, 'priority' => 100],
            $channel3->id => ['is_active' => false, 'priority' => 200],
        ]);

        $allChannels = $account->channels;
        $activeChannels = $account->activeChannels;

        $this->assertCount(3, $allChannels);
        $this->assertCount(2, $activeChannels);

        // Test that we can access pivot data
        foreach ($allChannels as $channel) {
            $this->assertNotNull($channel->pivot->priority);
            $this->assertIsInt($channel->pivot->is_active);
        }
    }

    public function test_password_without_object_caching_prevents_caching(): void
    {
        $account = PlatformAccount::factory()->create(['password' => 'original']);

        // Access password to potentially cache it
        $originalPassword = $account->password;
        $this->assertEquals('original', $originalPassword);

        // Update password directly in database
        $account->update(['password' => 'updated']);

        // Since withoutObjectCaching is used, the new value should be retrieved
        $account->refresh();
        $this->assertEquals('updated', $account->password);
    }
}
