<?php

namespace App\Livewire;

use App\Actions\CreateChannelAction;
use App\Models\Language;
use App\Models\PlatformAccount;
use App\Models\PlatformChannel;
use App\Models\PlatformInstance;
use App\Services\Platform\CommunityDirectory;
use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Validation\Rule;
use Livewire\Component;
use RuntimeException;

class Channels extends Component
{
    public ?int $managingChannelId = null;

    public bool $showCreateModal = false;

    public ?int $newCommunityId = null;

    public ?int $newPlatformInstanceId = null;

    /** @var array<int, array{id: int, name: string, title: string}> */
    public array $availableCommunities = [];

    public ?string $communityLoadError = null;

    public ?int $newLanguageId = null;

    public string $newDescription = '';

    public function toggle(int $channelId): void
    {
        $channel = PlatformChannel::findOrFail($channelId);
        $channel->is_active = ! $channel->is_active;
        $channel->save();
    }

    public function openCreateModal(): void
    {
        $this->reset(['newCommunityId', 'newPlatformInstanceId', 'newLanguageId', 'newDescription', 'availableCommunities', 'communityLoadError']);
        $this->resetErrorBag();
        $this->showCreateModal = true;
    }

    public function updatedNewPlatformInstanceId(?int $value): void
    {
        $this->reset(['newCommunityId', 'availableCommunities', 'communityLoadError']);

        if (! $value) {
            return;
        }

        $instance = PlatformInstance::find($value);

        if (! $instance) {
            return;
        }

        try {
            $this->availableCommunities = app(CommunityDirectory::class)->forInstance($instance);
        } catch (Exception $e) {
            $this->communityLoadError = 'Could not reach this instance to list its communities: '.$e->getMessage();
        }
    }

    public function refreshCommunities(): void
    {
        $instance = $this->newPlatformInstanceId ? PlatformInstance::find($this->newPlatformInstanceId) : null;

        if (! $instance) {
            return;
        }

        app(CommunityDirectory::class)->forget($instance);
        $this->updatedNewPlatformInstanceId($this->newPlatformInstanceId);
    }

    public function closeCreateModal(): void
    {
        $this->showCreateModal = false;
    }

    public function createChannel(CreateChannelAction $action): void
    {
        $this->validate([
            'newCommunityId' => [
                'required',
                'integer',
                Rule::in(collect($this->availableCommunities)->pluck('id')->all()),
                Rule::unique('platform_channels', 'channel_id')
                    ->where('platform_instance_id', $this->newPlatformInstanceId),
            ],
            'newPlatformInstanceId' => 'required|integer|exists:platform_instances,id',
            'newLanguageId' => 'nullable|integer|exists:languages,id',
        ], [
            'newCommunityId.in' => 'Select a community from this instance.',
            'newCommunityId.unique' => 'A channel for this community already exists.',
        ]);

        $name = collect($this->availableCommunities)->firstWhere('id', $this->newCommunityId)['name'] ?? null;

        try {
            $action->execute(
                $name,
                $this->newCommunityId,
                $this->newPlatformInstanceId,
                $this->newLanguageId,
                // Blade textarea binds an empty string when blank; the action expects null for "no description".
                $this->newDescription !== '' ? $this->newDescription : null,
            );
        } catch (UniqueConstraintViolationException $e) {
            $this->addError('newCommunityId', 'A channel for this community already exists.');

            return;
        } catch (RuntimeException $e) {
            $this->addError('newPlatformInstanceId', $e->getMessage());

            return;
        }

        $this->closeCreateModal();
    }

    public function openAccountModal(int $channelId): void
    {
        $this->managingChannelId = $channelId;
    }

    public function closeAccountModal(): void
    {
        $this->managingChannelId = null;
    }

    public function attachAccount(int $accountId): void
    {
        if (! $this->managingChannelId) {
            return;
        }

        $channel = PlatformChannel::findOrFail($this->managingChannelId);

        if (! $channel->platformAccounts()->where('platform_account_id', $accountId)->exists()) {
            $channel->platformAccounts()->attach($accountId, [
                'is_active' => true,
                'priority' => 1,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }
    }

    public function detachAccount(int $channelId, int $accountId): void
    {
        $channel = PlatformChannel::findOrFail($channelId);
        $channel->platformAccounts()->detach($accountId);
    }

    public function render(): View
    {
        $channels = PlatformChannel::with(['platformInstance', 'platformAccounts'])->orderBy('name')->get();
        $allAccounts = PlatformAccount::where('is_active', true)->get();

        $managingChannel = $this->managingChannelId
            ? PlatformChannel::with('platformAccounts')->find($this->managingChannelId)
            : null;

        $availableAccounts = $managingChannel
            ? $allAccounts->filter(fn ($account) => ! $managingChannel->platformAccounts->contains('id', $account->id))
            : collect();

        return view('livewire.channels', [
            'channels' => $channels,
            'managingChannel' => $managingChannel,
            'availableAccounts' => $availableAccounts,
            'platformInstances' => PlatformInstance::where('is_active', true)->orderBy('name')->get(),
            'languages' => Language::where('is_active', true)->orderBy('name')->get(),
        ])->layout('layouts.app');
    }
}
