<?php

namespace App\Livewire;

use App\Actions\CreateChannelAction;
use App\Actions\CreateFeedAction;
use App\Actions\CreatePlatformAccountAction;
use App\Actions\CreateRouteAction;
use App\Exceptions\PlatformAuthException;
use App\Jobs\ArticleDiscoveryJob;
use App\Jobs\SyncChannelPostsJob;
use App\Models\Feed;
use App\Models\Language;
use App\Models\PlatformAccount;
use App\Models\PlatformChannel;
use App\Models\PlatformInstance;
use App\Models\Route;
use App\Models\Setting;
use App\Services\OnboardingService;
use App\Services\Platform\CommunityDirectory;
use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\Validation\Rule;
use InvalidArgumentException;
use Livewire\Attributes\Locked;
use Livewire\Component;
use RuntimeException;

class Onboarding extends Component
{
    // Step tracking (1-6: welcome, platform, channel, feed, route, complete)
    public int $step = 1;

    // Platform form
    public string $instanceUrl = '';

    public string $username = '';

    public string $password = '';

    /** @var array<string, mixed>|null */
    public ?array $existingAccount = null;

    // Feed form
    public string $feedName = '';

    public string $feedProvider = 'vrt';

    public ?int $feedLanguageId = null;

    public string $feedDescription = '';

    // Channel form
    public ?int $channelCommunityId = null;

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

    public ?string $communityLoadError = null;

    public ?int $platformInstanceId = null;

    public ?int $channelLanguageId = null;

    public string $channelDescription = '';

    // Route form
    public ?int $routeFeedId = null;

    public ?int $routeChannelId = null;

    public int $routePriority = 50;

    // State
    /** @var array<string, string> */
    public array $formErrors = [];

    public bool $isLoading = false;

    #[Locked]
    public ?int $previousChannelLanguageId = null;

    protected CreatePlatformAccountAction $createPlatformAccountAction;

    protected CreateFeedAction $createFeedAction;

    protected CreateChannelAction $createChannelAction;

    protected CreateRouteAction $createRouteAction;

    public function boot(
        CreatePlatformAccountAction $createPlatformAccountAction,
        CreateFeedAction $createFeedAction,
        CreateChannelAction $createChannelAction,
        CreateRouteAction $createRouteAction,
    ): void {
        $this->createPlatformAccountAction = $createPlatformAccountAction;
        $this->createFeedAction = $createFeedAction;
        $this->createChannelAction = $createChannelAction;
        $this->createRouteAction = $createRouteAction;
    }

    public function mount(): void
    {
        // Check for existing platform account
        $account = PlatformAccount::where('is_active', true)->first();
        if ($account) {
            $this->existingAccount = [
                'id' => $account->id,
                'username' => $account->username,
                'instance_url' => $account->instance_url,
            ];
        }

        // Pre-fill feed form if exists
        $feed = Feed::where('is_active', true)->first();
        if ($feed) {
            $this->feedName = $feed->name;
            $this->feedProvider = $feed->provider ?? 'vrt';
            $this->feedLanguageId = $feed->language_id;
            $this->feedDescription = $feed->description ?? '';
        }

        // Pre-fill channel form if exists
        $channel = PlatformChannel::where('is_active', true)->first();
        if ($channel) {
            $this->platformInstanceId = $channel->platform_instance_id;
            $this->channelLanguageId = $channel->language_id;
            $this->channelDescription = $channel->description ?? '';
            $this->loadCommunities();
            $this->channelCommunityId = $channel->channel_id;
        }

        // Pre-fill route form if exists
        $route = Route::where('is_active', true)->first();
        if ($route) {
            $this->routeFeedId = $route->feed_id;
            $this->routeChannelId = $route->platform_channel_id;
            $this->routePriority = $route->priority;
        }
    }

    public function goToStep(int $step): void
    {
        $this->step = $step;
        $this->formErrors = [];
    }

    public function nextStep(): void
    {
        $this->step++;
        $this->formErrors = [];

        // When entering feed step, inherit language from channel
        if ($this->step === 4 && $this->channelLanguageId) {
            $this->feedLanguageId = $this->channelLanguageId;
        }
    }

    public function previousStep(): void
    {
        if ($this->step > 1) {
            $this->step--;
            $this->formErrors = [];
        }
    }

    public function continueWithExistingAccount(): void
    {
        $this->nextStep();
    }

    public function deleteAccount(): void
    {
        if ($this->existingAccount) {
            PlatformAccount::destroy($this->existingAccount['id']);
            $this->existingAccount = null;
        }
    }

    public function createPlatformAccount(): void
    {
        $this->formErrors = [];
        $this->isLoading = true;

        $this->validate([
            'instanceUrl' => 'required|string|max:255|regex:/^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$/',
            'username' => 'required|string|max:255',
            'password' => 'required|string|min:6',
        ], [
            'instanceUrl.regex' => 'Please enter a valid domain name (e.g., lemmy.world, belgae.social)',
        ]);

        try {
            $platformAccount = $this->createPlatformAccountAction->execute(
                $this->instanceUrl,
                $this->username,
                $this->password,
            );

            $this->existingAccount = [
                'id' => $platformAccount->id,
                'username' => $platformAccount->username,
                'instance_url' => $platformAccount->instance_url,
            ];

            $this->nextStep();
        } catch (PlatformAuthException $e) {
            $message = $e->getMessage();
            if (str_contains($message, 'Rate limited by')) {
                $this->formErrors['general'] = $message;
            } elseif (str_contains($message, 'Connection failed')) {
                $this->formErrors['general'] = 'Unable to connect to the Lemmy instance. Please check the URL and try again.';
            } else {
                $this->formErrors['general'] = 'Invalid username or password. Please check your credentials and try again.';
            }
        } catch (Exception $e) {
            logger()->error('Lemmy platform account creation failed', [
                'instance_url' => 'https://'.$this->instanceUrl,
                'username' => $this->username,
                'error' => $e->getMessage(),
                'class' => get_class($e),
            ]);
            $this->formErrors['general'] = 'An error occurred while setting up your account. Please try again.';
        } finally {
            $this->isLoading = false;
        }
    }

    public function createFeed(): void
    {
        $this->formErrors = [];
        $this->isLoading = true;

        // Get available provider codes for validation
        $availableProviders = collect($this->getProvidersForLanguage())->pluck('code')->implode(',');

        $this->validate([
            'feedName' => 'required|string|max:255',
            'feedProvider' => "required|in:{$availableProviders}",
            'feedLanguageId' => 'required|exists:languages,id',
            'feedDescription' => 'nullable|string|max:1000',
        ]);

        try {
            $this->createFeedAction->execute(
                $this->feedName,
                $this->feedProvider,
                $this->feedLanguageId,
                $this->feedDescription ?: null,
            );

            $this->nextStep();
        } catch (InvalidArgumentException $e) {
            $this->formErrors['general'] = 'Invalid provider and language combination.';
        } catch (Exception $e) {
            $this->formErrors['general'] = 'Failed to create feed. Please try again.';
        } finally {
            $this->isLoading = false;
        }
    }

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

        if ($value) {
            $this->loadCommunities();
        }
    }

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

        if (! $instance) {
            return;
        }

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

    private function loadCommunities(): void
    {
        $this->availableCommunities = [];
        $this->communityLoadError = null;

        $instance = $this->platformInstanceId ? PlatformInstance::find($this->platformInstanceId) : null;

        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 createChannel(): void
    {
        $this->formErrors = [];
        $this->isLoading = true;

        $this->validate([
            'channelCommunityId' => [
                'required',
                'integer',
                Rule::in(collect($this->availableCommunities)->pluck('id')->all()),
            ],
            'platformInstanceId' => 'required|exists:platform_instances,id',
            'channelLanguageId' => 'required|exists:languages,id',
            'channelDescription' => 'nullable|string|max:1000',
        ], [
            'channelCommunityId.in' => 'Select a community from this instance.',
        ]);

        // If language changed, reset feed form
        if ($this->previousChannelLanguageId !== null && $this->previousChannelLanguageId !== $this->channelLanguageId) {
            $this->feedName = '';
            $this->feedProvider = '';
            $this->feedDescription = '';
            $this->routeFeedId = null;
            $this->routeChannelId = null;
        }
        $this->previousChannelLanguageId = $this->channelLanguageId;

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

        try {
            $channel = $this->createChannelAction->execute(
                $name,
                (int) $this->channelCommunityId,
                (int) $this->platformInstanceId,
                $this->channelLanguageId !== null ? (int) $this->channelLanguageId : null,
                $this->channelDescription ?: null,
            );

            // Sync existing posts from this channel for duplicate detection
            SyncChannelPostsJob::dispatch($channel);

            $this->nextStep();
        } catch (RuntimeException $e) {
            $this->formErrors['general'] = $e->getMessage();
        } catch (Exception $e) {
            $this->formErrors['general'] = 'Failed to create channel. Please try again.';
        } finally {
            $this->isLoading = false;
        }
    }

    public function createRoute(): void
    {
        $this->formErrors = [];
        $this->isLoading = true;

        $this->validate([
            'routeFeedId' => 'required|exists:feeds,id',
            'routeChannelId' => 'required|exists:platform_channels,id',
            'routePriority' => 'nullable|integer|min:1|max:100',
        ]);

        try {
            $this->createRouteAction->execute(
                $this->routeFeedId,
                $this->routeChannelId,
                $this->routePriority,
            );

            // Trigger article discovery
            ArticleDiscoveryJob::dispatch();

            $this->nextStep();
        } catch (Exception $e) {
            $this->formErrors['general'] = 'Failed to create route. Please try again.';
        } finally {
            $this->isLoading = false;
        }
    }

    public function completeOnboarding(): void
    {
        Setting::updateOrCreate(
            ['key' => 'onboarding_completed'],
            ['value' => now()->toIso8601String()]
        );

        app(OnboardingService::class)->clearCache();

        $this->redirect(route('dashboard'));
    }

    /**
     * Get language codes that have at least one active provider.
     */
    /**
     * @return list<string>
     */
    public function getAvailableLanguageCodes(): array
    {
        $providers = config('feed.providers', []);
        $languageCodes = [];

        foreach ($providers as $provider) {
            if (! ($provider['is_active'] ?? false)) {
                continue;
            }
            foreach (array_keys($provider['languages'] ?? []) as $code) {
                $languageCodes[(string) $code] = true;
            }
        }

        return array_keys($languageCodes);
    }

    /**
     * Get providers available for the current channel language.
     */
    /**
     * @return array<int, array<string, string>>
     */
    public function getProvidersForLanguage(): array
    {
        if (! $this->channelLanguageId) {
            return [];
        }

        $language = Language::find($this->channelLanguageId);
        if (! $language) {
            return [];
        }

        $langCode = $language->short_code;
        $providers = config('feed.providers', []);
        $available = [];

        foreach ($providers as $key => $provider) {
            if (! ($provider['is_active'] ?? false)) {
                continue;
            }
            if (isset($provider['languages'][$langCode])) {
                $available[] = [
                    'code' => $provider['code'],
                    'name' => $provider['name'],
                    'description' => $provider['description'] ?? '',
                ];
            }
        }

        return $available;
    }

    /**
     * Get the current channel language model.
     */
    public function getChannelLanguage(): ?Language
    {
        if (! $this->channelLanguageId) {
            return null;
        }

        return Language::find($this->channelLanguageId);
    }

    public function render(): View
    {
        // For channel step: only show languages that have providers
        $availableCodes = $this->getAvailableLanguageCodes();
        $wizardLanguages = Language::where('is_active', true)
            ->whereIn('short_code', $availableCodes)
            ->orderBy('name')
            ->get();

        $platformInstances = PlatformInstance::where('is_active', true)->orderBy('name')->get();
        $feeds = Feed::with('language')->where('is_active', true)->orderBy('name')->get();
        $channels = PlatformChannel::with('language')->where('is_active', true)->orderBy('name')->get();

        // For feed step: only show providers for the channel's language
        $feedProviders = collect($this->getProvidersForLanguage());

        // Get channel language for display
        $channelLanguage = $this->getChannelLanguage();

        return view('livewire.onboarding', [
            'wizardLanguages' => $wizardLanguages,
            'platformInstances' => $platformInstances,
            'feeds' => $feeds,
            'channels' => $channels,
            'feedProviders' => $feedProviders,
            'channelLanguage' => $channelLanguage,
        ])->layout('layouts.onboarding');
    }
}
