<?php

namespace Tests\Unit\Modules\Lemmy\Services;

use App\Models\PlatformChannel;
use App\Modules\Lemmy\Services\LemmyApiService;
use Exception;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class LemmyApiServiceTest extends TestCase
{
    use RefreshDatabase;

    private ?PlatformChannel $channel = null;

    private function syncChannel(): PlatformChannel
    {
        return $this->channel ??= PlatformChannel::factory()->create();
    }

    public function test_constructor_sets_instance(): void
    {
        $service = new LemmyApiService('lemmy.world');

        $reflection = new \ReflectionClass($service);
        $property = $reflection->getProperty('instance');
        $property->setAccessible(true);

        $this->assertEquals('lemmy.world', $property->getValue($service));
    }

    public function test_login_with_https_success(): void
    {
        Http::fake([
            'https://lemmy.world/api/v3/user/login' => Http::response(['jwt' => 'test-token'], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $token = $service->login('user', 'pass');

        $this->assertEquals('test-token', $token);

        Http::assertSent(function ($request) {
            return $request->url() === 'https://lemmy.world/api/v3/user/login'
                && $request['username_or_email'] === 'user'
                && $request['password'] === 'pass';
        });
    }

    public function test_login_falls_back_to_http_on_https_failure(): void
    {
        Http::fake([
            'https://lemmy.world/api/v3/user/login' => Http::response('', 500),
            'http://lemmy.world/api/v3/user/login' => Http::response(['jwt' => 'http-token'], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $token = $service->login('user', 'pass');

        $this->assertEquals('http-token', $token);

        Http::assertSentCount(2);
    }

    public function test_login_with_explicit_http_scheme(): void
    {
        Http::fake([
            'http://localhost/api/v3/user/login' => Http::response(['jwt' => 'local-token'], 200),
        ]);

        $service = new LemmyApiService('http://localhost');
        $token = $service->login('user', 'pass');

        $this->assertEquals('local-token', $token);

        Http::assertSent(function ($request) {
            return $request->url() === 'http://localhost/api/v3/user/login';
        });
    }

    public function test_login_with_explicit_https_scheme(): void
    {
        Http::fake([
            'https://secure.lemmy/api/v3/user/login' => Http::response(['jwt' => 'secure-token'], 200),
        ]);

        $service = new LemmyApiService('https://secure.lemmy');
        $token = $service->login('user', 'pass');

        $this->assertEquals('secure-token', $token);

        Http::assertSent(function ($request) {
            return $request->url() === 'https://secure.lemmy/api/v3/user/login';
        });
    }

    public function test_login_returns_null_on_unsuccessful_response(): void
    {
        Http::fake([
            '*' => Http::response(['error' => 'Invalid credentials'], 401),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $token = $service->login('user', 'wrong');

        $this->assertNull($token);
    }

    public function test_login_handles_rate_limit_error(): void
    {
        Http::fake([
            '*' => Http::response('{"error":"rate_limit_error"}', 429),
        ]);

        $service = new LemmyApiService('lemmy.world');

        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Rate limited');

        $service->login('user', 'pass');
    }

    public function test_login_returns_null_when_jwt_missing_from_response(): void
    {
        Http::fake([
            '*' => Http::response(['success' => true], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $token = $service->login('user', 'pass');

        $this->assertNull($token);
    }

    public function test_login_handles_exception_and_throws(): void
    {
        Http::fake(function () {
            throw new Exception('Network error');
        });

        $service = new LemmyApiService('lemmy.world');

        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Connection failed');

        $service->login('user', 'pass');
    }

    public function test_get_community_id_success(): void
    {
        Http::fake([
            '*' => Http::response([
                'community_view' => [
                    'community' => ['id' => 123],
                ],
            ], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $id = $service->getCommunityId('test-community', 'token');

        $this->assertEquals(123, $id);

        Http::assertSent(function ($request) {
            return str_contains($request->url(), '/api/v3/community')
                && str_contains($request->url(), 'name=test-community')
                && $request->header('Authorization')[0] === 'Bearer token';
        });
    }

    public function test_list_communities_returns_local_communities(): void
    {
        Http::fake(['*' => Http::response(['communities' => [
            ['community' => ['id' => 8, 'name' => 'news', 'title' => 'News']],
            ['community' => ['id' => 9, 'name' => 'memes', 'title' => 'Memes']],
        ]], 200)]);

        $communities = (new LemmyApiService('lemmy.world'))->listCommunities();

        $this->assertSame(
            [['id' => 9, 'name' => 'memes', 'title' => 'Memes'], ['id' => 8, 'name' => 'news', 'title' => 'News']],
            $communities
        );
    }

    public function test_list_communities_omits_removed_and_deleted(): void
    {
        Http::fake(['*' => Http::response(['communities' => [
            ['community' => ['id' => 8, 'name' => 'news', 'title' => 'News']],
            ['community' => ['id' => 9, 'name' => 'gone', 'title' => 'Gone', 'removed' => true]],
            ['community' => ['id' => 10, 'name' => 'bye', 'title' => 'Bye', 'deleted' => true]],
        ]], 200)]);

        $this->assertSame([8], collect((new LemmyApiService('lemmy.world'))->listCommunities())->pluck('id')->all());
    }

    public function test_list_communities_throws_on_unsuccessful_response(): void
    {
        Http::fake(['*' => Http::response('nope', 500)]);

        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Failed to list communities: 500');

        (new LemmyApiService('lemmy.world'))->listCommunities();
    }

    public function test_get_community_id_throws_on_unsuccessful_response(): void
    {
        Http::fake([
            '*' => Http::response('Not found', 404),
        ]);

        $service = new LemmyApiService('lemmy.world');

        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Failed to fetch community: 404');

        $service->getCommunityId('missing', 'token');
    }

    public function test_get_community_id_throws_when_community_not_in_response(): void
    {
        Http::fake([
            '*' => Http::response(['success' => true], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');

        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Community not found');

        $service->getCommunityId('test', 'token');
    }

    public function test_sync_channel_posts_success(): void
    {
        Http::fake([
            '*' => Http::response([
                'posts' => [
                    [
                        'post' => [
                            'id' => 1,
                            'url' => 'https://example.com/1',
                            'name' => 'Post 1',
                            'published' => '2024-01-01T00:00:00Z',
                        ],
                    ],
                    [
                        'post' => [
                            'id' => 2,
                            'url' => 'https://example.com/2',
                            'name' => 'Post 2',
                            'published' => '2024-01-02T00:00:00Z',
                        ],
                    ],
                ],
            ], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $service->syncChannelPosts('token', $this->syncChannel(), 42);

        Http::assertSent(function ($request) {
            return str_contains($request->url(), '/api/v3/post/list')
                && str_contains($request->url(), 'community_id=42')
                && str_contains($request->url(), 'limit=50')
                && str_contains($request->url(), 'sort=New');
        });

        // Verify posts were stored in the database
        $this->assertDatabaseHas('platform_channel_posts', [
            'platform_channel_id' => $this->syncChannel()->id,
            'post_id' => '1',
            'url' => 'https://example.com/1',
            'title' => 'Post 1',
        ]);

        $this->assertDatabaseHas('platform_channel_posts', [
            'platform_channel_id' => $this->syncChannel()->id,
            'post_id' => '2',
            'url' => 'https://example.com/2',
            'title' => 'Post 2',
        ]);
    }

    public function test_sync_channel_posts_handles_unsuccessful_response(): void
    {
        Http::fake([
            '*' => Http::response('Error', 500),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $service->syncChannelPosts('token', $this->syncChannel(), 42);

        Http::assertSentCount(1);
        $this->assertDatabaseCount('platform_channel_posts', 0);
    }

    public function test_sync_channel_posts_handles_exception(): void
    {
        Http::fake(function () {
            throw new Exception('Network error');
        });

        $service = new LemmyApiService('lemmy.world');
        $service->syncChannelPosts('token', $this->syncChannel(), 42);

        // Assert that the method completes without throwing
        $this->assertTrue(true);
    }

    public function test_create_post_with_all_parameters(): void
    {
        Http::fake([
            '*' => Http::response(['post_view' => ['post' => ['id' => 999]]], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $result = $service->createPost(
            'token',
            'Test Title',
            'Test Body',
            42,
            'https://example.com',
            'https://example.com/thumb.jpg',
            5
        );

        $this->assertEquals(['post_view' => ['post' => ['id' => 999]]], $result);

        Http::assertSent(function ($request) {
            $data = $request->data();

            return $request->url() === 'https://lemmy.world/api/v3/post'
                && $data['name'] === 'Test Title'
                && $data['body'] === 'Test Body'
                && $data['community_id'] === 42
                && $data['url'] === 'https://example.com'
                && $data['custom_thumbnail'] === 'https://example.com/thumb.jpg'
                && $data['language_id'] === 5;
        });
    }

    public function test_create_post_with_minimal_parameters(): void
    {
        Http::fake([
            '*' => Http::response(['post_view' => ['post' => ['id' => 888]]], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $result = $service->createPost(
            'token',
            'Title Only',
            'Body Only',
            42
        );

        $this->assertEquals(['post_view' => ['post' => ['id' => 888]]], $result);

        Http::assertSent(function ($request) {
            $data = $request->data();

            return $request->url() === 'https://lemmy.world/api/v3/post'
                && $data['name'] === 'Title Only'
                && $data['body'] === 'Body Only'
                && $data['community_id'] === 42
                && ! isset($data['url'])
                && ! isset($data['custom_thumbnail'])
                && ! isset($data['language_id']);
        });
    }

    public function test_create_post_throws_on_unsuccessful_response(): void
    {
        Http::fake([
            '*' => Http::response('Forbidden', 403),
        ]);

        $service = new LemmyApiService('lemmy.world');

        $this->expectException(Exception::class);
        $this->expectExceptionMessage('Failed to create post: 403');

        $service->createPost('token', 'Title', 'Body', 42);
    }

    public function test_get_languages_success(): void
    {
        Http::fake([
            '*' => Http::response([
                'all_languages' => [
                    ['id' => 1, 'code' => 'en', 'name' => 'English'],
                    ['id' => 2, 'code' => 'fr', 'name' => 'French'],
                ],
            ], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $languages = $service->getLanguages();

        $this->assertCount(2, $languages);
        $this->assertEquals('en', $languages[0]['code']);
        $this->assertEquals('fr', $languages[1]['code']);

        Http::assertSent(function ($request) {
            return str_contains($request->url(), '/api/v3/site');
        });
    }

    public function test_get_languages_returns_empty_array_on_failure(): void
    {
        Http::fake([
            '*' => Http::response('Error', 500),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $languages = $service->getLanguages();

        $this->assertEquals([], $languages);
    }

    public function test_get_languages_handles_exception(): void
    {
        Http::fake(function () {
            throw new Exception('Network error');
        });

        $service = new LemmyApiService('lemmy.world');
        $languages = $service->getLanguages();

        $this->assertEquals([], $languages);
    }

    public function test_get_languages_returns_empty_when_all_languages_missing(): void
    {
        Http::fake([
            '*' => Http::response(['site_view' => []], 200),
        ]);

        $service = new LemmyApiService('lemmy.world');
        $languages = $service->getLanguages();

        $this->assertEquals([], $languages);
    }
}
