<?php

namespace App\Modules\Lemmy\Services;

use App\Models\PlatformChannel;
use App\Models\PlatformChannelPost;
use App\Modules\Lemmy\LemmyRequest;
use Exception;

class LemmyApiService
{
    private string $instance;

    public function __construct(string $instance)
    {
        $this->instance = $instance;
    }

    public function login(string $username, string $password): ?string
    {
        // Try HTTPS first; on failure, optionally retry with HTTP to support dev instances
        $schemesToTry = [];
        if (preg_match('/^https?:\/\//i', $this->instance)) {
            // Preserve user-provided scheme as first try
            $schemesToTry[] = strtolower(str_starts_with($this->instance, 'http://') ? 'http' : 'https');
        } else {
            // Default order: https then http
            $schemesToTry = ['https', 'http'];
        }

        foreach ($schemesToTry as $idx => $scheme) {
            try {
                $request = new LemmyRequest($this->instance);
                // ensure scheme used matches current attempt
                $request = $request->withScheme($scheme);

                $response = $request->post('user/login', [
                    'username_or_email' => $username,
                    'password' => $password,
                ]);

                if (! $response->successful()) {
                    $responseBody = $response->body();
                    logger()->error('Lemmy login failed', [
                        'status' => $response->status(),
                        'body' => $responseBody,
                        'scheme' => $scheme,
                    ]);

                    // Check if it's a rate limit error
                    if (str_contains($responseBody, 'rate_limit_error')) {
                        throw new Exception('Rate limited by Lemmy instance. Please wait a moment and try again.');
                    }

                    // If first attempt failed and there is another scheme to try, continue loop
                    if ($idx === 0 && count($schemesToTry) > 1) {
                        continue;
                    }

                    return null;
                }

                $data = $response->json();

                return $data['jwt'] ?? null;
            } catch (Exception $e) {
                // Re-throw rate limit exceptions immediately
                if (str_contains($e->getMessage(), 'Rate limited')) {
                    throw $e;
                }

                logger()->error('Lemmy login exception', ['error' => $e->getMessage(), 'scheme' => $scheme]);
                // If this was the first attempt and HTTPS, try HTTP next
                if ($idx === 0 && in_array('http', $schemesToTry, true)) {
                    continue;
                }
                // Connection failed - throw exception to distinguish from auth failure
                throw new Exception('Connection failed: '.$e->getMessage());
            }
        }

        // If we get here without a token, it's an auth failure (not connection)
        return null;
    }

    /**
     * @return array<int, array{id: int, name: string, title: string}>
     */
    public function listCommunities(?string $token = null): array
    {
        $request = new LemmyRequest($this->instance, $token);
        $response = $request->get('community/list', [
            'type_' => 'Local',
            'limit' => 50,
            'sort' => 'TopAll',
        ]);

        if (! $response->successful()) {
            throw new Exception('Failed to list communities: '.$response->status());
        }

        /** @var array<int, array<string, mixed>> $communities */
        $communities = $response->json('communities') ?? [];

        return collect($communities)
            ->pluck('community')
            ->reject(fn ($community) => ($community['removed'] ?? false) || ($community['deleted'] ?? false))
            ->map(fn ($community) => [
                'id' => (int) $community['id'],
                'name' => (string) $community['name'],
                'title' => (string) ($community['title'] ?? $community['name']),
            ])
            ->sortBy('name')
            ->values()
            ->all();
    }

    public function getCommunityId(string $communityName, string $token): int
    {
        try {
            $request = new LemmyRequest($this->instance, $token);
            $response = $request->get('community', ['name' => $communityName]);

            if (! $response->successful()) {
                throw new Exception('Failed to fetch community: '.$response->status());
            }

            $data = $response->json();

            return $data['community_view']['community']['id'] ?? throw new Exception('Community not found');
        } catch (Exception $e) {
            logger()->error('Community lookup failed', ['error' => $e->getMessage()]);
            throw $e;
        }
    }

    public function syncChannelPosts(string $token, PlatformChannel $channel, int $communityId): void
    {
        try {
            $request = new LemmyRequest($this->instance, $token);
            $response = $request->get('post/list', [
                'community_id' => $communityId,
                'limit' => 50,
                'sort' => 'New',
            ]);

            if (! $response->successful()) {
                logger()->warning('Failed to sync channel posts', [
                    'status' => $response->status(),
                    'platform_channel_id' => $channel->id,
                ]);

                return;
            }

            $data = $response->json();
            $posts = $data['posts'] ?? [];

            foreach ($posts as $postData) {
                $post = $postData['post'];

                PlatformChannelPost::storePost(
                    $channel,
                    (string) $post['id'],
                    $post['url'] ?? null,
                    $post['name'] ?? null,
                    isset($post['published']) ? new \DateTime($post['published']) : null
                );
            }

            logger()->info('Synced channel posts', [
                'platform_channel_id' => $channel->id,
                'posts_count' => count($posts),
            ]);

        } catch (Exception $e) {
            logger()->error('Exception while syncing channel posts', [
                'error' => $e->getMessage(),
                'platform_channel_id' => $channel->id,
            ]);
        }
    }

    /**
     * @return array<string, mixed>
     */
    public function createPost(string $token, string $title, string $body, int $platformChannelId, ?string $url = null, ?string $thumbnail = null, ?int $languageId = null): array
    {
        try {
            $request = new LemmyRequest($this->instance, $token);

            $postData = [
                'name' => $title,
                'body' => $body,
                'community_id' => $platformChannelId,
            ];

            if ($url) {
                $postData['url'] = $url;
            }

            if ($thumbnail) {
                $postData['custom_thumbnail'] = $thumbnail;
            }

            if ($languageId) {
                $postData['language_id'] = $languageId;
            }

            $response = $request->post('post', $postData);

            if (! $response->successful()) {
                throw new Exception('Failed to create post: '.$response->status().' - '.$response->body());
            }

            return $response->json();
        } catch (Exception $e) {
            logger()->error('Post creation failed', ['error' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @return array<int, mixed>
     */
    public function getLanguages(): array
    {
        try {
            $request = new LemmyRequest($this->instance);
            $response = $request->get('site');

            if (! $response->successful()) {
                logger()->warning('Failed to fetch site languages', [
                    'status' => $response->status(),
                ]);

                return [];
            }

            $data = $response->json();

            return $data['all_languages'] ?? [];
        } catch (Exception $e) {
            logger()->error('Exception while fetching languages', [
                'error' => $e->getMessage(),
            ]);

            return [];
        }
    }
}
