<?php

namespace Tests\Feature\Livewire;

use App\Livewire\Channels;
use App\Models\Language;
use App\Models\PlatformAccount;
use App\Models\PlatformChannel;
use App\Models\PlatformInstance;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Factory;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Livewire\Livewire;
use Tests\TestCase;

class ChannelsTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        Cache::flush();
        $this->fakeCommunities();
    }

    private function fakeCommunities(): void
    {
        Http::fake(['*/api/v3/community/list*' => Http::response([
            'communities' => [
                ['community' => ['id' => 8, 'name' => 'tech_community', 'title' => 'Tech Community']],
                ['community' => ['id' => 9, 'name' => 'other_community', 'title' => 'Other Community']],
            ],
        ])]);
    }

    private function instanceWithActiveAccount(): PlatformInstance
    {
        $instance = PlatformInstance::factory()->create(['url' => 'https://lemmy.world']);
        PlatformAccount::factory()->create([
            'instance_url' => 'https://lemmy.world',
            'is_active' => true,
        ]);

        return $instance;
    }

    public function test_add_button_renders_when_channels_exist(): void
    {
        PlatformChannel::factory()->create(['name' => 'existing_channel']);

        Livewire::test(Channels::class)
            ->assertSee('Add Channel')
            ->assertSee('existing_channel');
    }

    public function test_add_button_renders_in_empty_state(): void
    {
        Livewire::test(Channels::class)
            ->assertSee('No channels')
            ->assertSee('Add Channel');
    }

    public function test_open_create_modal_shows_modal(): void
    {
        Livewire::test(Channels::class)
            ->assertSet('showCreateModal', false)
            ->call('openCreateModal')
            ->assertSet('showCreateModal', true);
    }

    public function test_create_channel_requires_community(): void
    {
        $instance = $this->instanceWithActiveAccount();

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->call('createChannel')
            ->assertHasErrors(['newCommunityId' => 'required']);
    }

    public function test_selecting_an_instance_loads_its_communities(): void
    {
        $instance = $this->instanceWithActiveAccount();

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->assertSet('communityLoadError', null)
            ->assertCount('availableCommunities', 2);
    }

    public function test_unreachable_instance_surfaces_an_error_and_no_communities(): void
    {
        $instance = PlatformInstance::factory()->create(['url' => 'https://unreachable.test']);
        PlatformAccount::factory()->create(['instance_url' => 'https://unreachable.test', 'is_active' => true]);
        Cache::flush();

        // Http::fake() merges stubs, so setUp's success stub would still win —
        // swap the whole fake out instead.
        app()->forgetInstance(Factory::class);
        Http::swap(new Factory);
        Http::fake(['*' => Http::response('nope', 500)]);

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->assertSet('availableCommunities', [])
            ->assertNotSet('communityLoadError', null);
    }

    public function test_create_channel_requires_platform_instance(): void
    {
        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newCommunityId', 8)
            ->call('createChannel')
            ->assertHasErrors(['newPlatformInstanceId' => 'required']);
    }

    public function test_create_channel_succeeds_and_attaches_account(): void
    {
        $instance = $this->instanceWithActiveAccount();
        $language = Language::factory()->create();

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->set('newCommunityId', 8)
            ->set('newLanguageId', $language->id)
            ->set('newDescription', 'A tech community')
            ->call('createChannel')
            ->assertHasNoErrors()
            ->assertSet('showCreateModal', false);

        $this->assertDatabaseHas('platform_channels', [
            'name' => 'tech_community',
            'display_name' => 'Tech_community',
            'platform_instance_id' => $instance->id,
            'language_id' => $language->id,
            'description' => 'A tech community',
            'is_active' => true,
        ]);

        $channel = PlatformChannel::where('name', 'tech_community')->firstOrFail();
        $this->assertCount(1, $channel->platformAccounts);
    }

    public function test_create_channel_leaves_description_null_when_blank(): void
    {
        $instance = $this->instanceWithActiveAccount();

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->set('newCommunityId', 8)
            ->call('createChannel')
            ->assertHasNoErrors();

        $this->assertDatabaseHas('platform_channels', [
            'name' => 'tech_community',
            'description' => null,
        ]);
    }

    public function test_create_channel_rejects_a_community_not_on_the_instance(): void
    {
        $instance = $this->instanceWithActiveAccount();

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->set('newCommunityId', 4242)
            ->call('createChannel')
            ->assertHasErrors(['newCommunityId' => 'in']);

        $this->assertDatabaseCount('platform_channels', 0);
    }

    public function test_create_channel_rejects_duplicate_community_on_same_instance(): void
    {
        $instance = $this->instanceWithActiveAccount();
        PlatformChannel::factory()->create([
            'platform_instance_id' => $instance->id,
            'name' => 'tech_community',
            'channel_id' => 8,
        ]);

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->set('newCommunityId', 8)
            ->call('createChannel')
            ->assertHasErrors(['newCommunityId' => 'unique'])
            ->assertSet('showCreateModal', true);

        $this->assertDatabaseCount('platform_channels', 1);
    }

    public function test_create_channel_allows_same_community_on_different_instance(): void
    {
        $instanceA = $this->instanceWithActiveAccount();
        $instanceB = PlatformInstance::factory()->create(['url' => 'https://lemmy.other']);
        PlatformAccount::factory()->create([
            'instance_url' => 'https://lemmy.other',
            'is_active' => true,
        ]);
        PlatformChannel::factory()->create([
            'platform_instance_id' => $instanceB->id,
            'name' => 'tech_community',
            'channel_id' => 8,
        ]);

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instanceA->id)
            ->set('newCommunityId', 8)
            ->call('createChannel')
            ->assertHasNoErrors();

        $this->assertDatabaseCount('platform_channels', 2);
    }

    public function test_create_channel_surfaces_no_active_accounts_error(): void
    {
        // Instance exists but has no active account for its url.
        $instance = PlatformInstance::factory()->create(['url' => 'https://lemmy.world']);

        Livewire::test(Channels::class)
            ->call('openCreateModal')
            ->set('newPlatformInstanceId', $instance->id)
            ->set('newCommunityId', 8)
            ->call('createChannel')
            ->assertHasErrors('newPlatformInstanceId')
            ->assertSet('showCreateModal', true);

        $this->assertDatabaseCount('platform_channels', 0);
    }

    public function test_toggle_flips_active_state(): void
    {
        $channel = PlatformChannel::factory()->create(['is_active' => true]);

        Livewire::test(Channels::class)
            ->call('toggle', $channel->id);

        $this->assertFalse($channel->fresh()->is_active);
    }
}
