<?php

namespace Tests\Unit\Models;

use App\Enums\FeedColorEnum;
use App\Models\Article;
use App\Models\Feed;
use App\Models\Language;
use App\Models\PlatformChannel;
use App\Models\Route;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class FeedTest extends TestCase
{
    use RefreshDatabase;

    public function test_fillable_fields(): void
    {
        $fillableFields = ['name', 'url', 'type', 'provider', 'language_id', 'description', 'color', 'settings', 'is_active', 'last_fetched_at'];
        $feed = new Feed;

        $this->assertEquals($fillableFields, $feed->getFillable());
    }

    public function test_display_color_falls_back_to_a_deterministic_color(): void
    {
        $feed = Feed::factory()->create(['color' => null]);

        $this->assertSame(FeedColorEnum::forId($feed->id), $feed->displayColor());
        $this->assertSame(FeedColorEnum::forId($feed->id), $feed->fresh()->displayColor());
    }

    public function test_display_color_prefers_the_stored_color(): void
    {
        $feed = Feed::factory()->create(['color' => FeedColorEnum::ROSE]);

        $this->assertSame(FeedColorEnum::ROSE, $feed->fresh()->displayColor());
    }

    public function test_color_fallback_wraps_around_the_palette(): void
    {
        $paletteSize = count(FeedColorEnum::cases());

        $this->assertSame(FeedColorEnum::forId(0), FeedColorEnum::forId($paletteSize));
    }

    public function test_every_palette_dot_class_is_safelisted_for_tailwind(): void
    {
        $config = (string) file_get_contents(base_path('tailwind.config.js'));

        foreach (FeedColorEnum::cases() as $color) {
            $this->assertStringContainsString("'{$color->dotClass()}'", $config);
        }
    }

    public function test_casts_settings_to_array(): void
    {
        $settings = ['key1' => 'value1', 'key2' => ['nested' => 'value']];

        $feed = Feed::factory()->create(['settings' => $settings]);

        $this->assertIsArray($feed->settings);
        $this->assertEquals($settings, $feed->settings);
    }

    public function test_casts_is_active_to_boolean(): void
    {
        $feed = Feed::factory()->create(['is_active' => '1']);

        $this->assertIsBool($feed->is_active);
        $this->assertTrue($feed->is_active);

        $feed->update(['is_active' => '0']);
        $feed->refresh();

        $this->assertIsBool($feed->is_active);
        $this->assertFalse($feed->is_active);
    }

    public function test_casts_last_fetched_at_to_datetime(): void
    {
        $timestamp = now()->subHours(2);
        $feed = Feed::factory()->create(['last_fetched_at' => $timestamp]);

        $this->assertInstanceOf(Carbon::class, $feed->last_fetched_at);
        $this->assertEquals($timestamp->format('Y-m-d H:i:s'), $feed->last_fetched_at->format('Y-m-d H:i:s'));
    }

    public function test_type_display_attribute(): void
    {
        $websiteFeed = Feed::factory()->create(['type' => 'website']);
        $rssFeed = Feed::factory()->create(['type' => 'rss']);

        $this->assertEquals('Website', $websiteFeed->type_display);
        $this->assertEquals('RSS Feed', $rssFeed->type_display);
    }

    public function test_status_attribute_inactive_feed(): void
    {
        $feed = Feed::factory()->create(['is_active' => false]);

        $this->assertEquals('Inactive', $feed->status);
    }

    public function test_status_attribute_never_fetched(): void
    {
        $feed = Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => null,
        ]);

        $this->assertEquals('Never fetched', $feed->status);
    }

    public function test_status_attribute_recently_fetched(): void
    {
        $feed = Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => now()->subHour(),
        ]);

        $this->assertEquals('Recently fetched', $feed->status);
    }

    public function test_status_attribute_fetched_hours_ago(): void
    {
        $feed = Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => now()->subHours(5)->startOfHour(),
        ]);

        $this->assertStringContainsString('Fetched', $feed->status);
        $this->assertStringContainsString('ago', $feed->status);
    }

    public function test_status_attribute_fetched_days_ago(): void
    {
        $feed = Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => now()->subDays(3),
        ]);

        $this->assertStringStartsWith('Fetched', $feed->status);
        $this->assertStringContainsString('ago', $feed->status);
    }

    public function test_belongs_to_language_relationship(): void
    {
        $language = Language::factory()->create();
        $feed = Feed::factory()->create(['language_id' => $language->id]);

        $this->assertInstanceOf(Language::class, $feed->language);
        $this->assertEquals($language->id, $feed->language->id);
        $this->assertEquals($language->name, $feed->language->name);
    }

    public function test_has_many_articles_relationship(): void
    {
        $feed = Feed::factory()->create();

        $article1 = Article::factory()->create(['feed_id' => $feed->id]);
        $article2 = Article::factory()->create(['feed_id' => $feed->id]);

        // Create article for different feed
        $otherFeed = Feed::factory()->create();
        Article::factory()->create(['feed_id' => $otherFeed->id]);

        $articles = $feed->articles;

        $this->assertCount(2, $articles);
        $this->assertTrue($articles->contains('id', $article1->id));
        $this->assertTrue($articles->contains('id', $article2->id));
        $this->assertInstanceOf(Article::class, $articles->first());
    }

    public function test_belongs_to_many_channels_relationship(): void
    {
        $feed = Feed::factory()->create();
        $channel1 = PlatformChannel::factory()->create();
        $channel2 = PlatformChannel::factory()->create();

        // Create routes (which act as pivot records)
        Route::create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel1->id,
            'is_active' => true,
            'priority' => 100,
        ]);

        Route::create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel2->id,
            'is_active' => false,
            'priority' => 50,
        ]);

        $channels = $feed->channels;

        $this->assertCount(2, $channels);
        $this->assertTrue($channels->contains('id', $channel1->id));
        $this->assertTrue($channels->contains('id', $channel2->id));

        // Test pivot data
        $channel1FromRelation = $channels->find($channel1->id);
        $this->assertEquals(1, $channel1FromRelation->pivot->is_active);
        $this->assertEquals(100, $channel1FromRelation->pivot->priority);
    }

    public function test_active_channels_relationship(): void
    {
        $feed = Feed::factory()->create();
        $activeChannel1 = PlatformChannel::factory()->create();
        $activeChannel2 = PlatformChannel::factory()->create();
        $inactiveChannel = PlatformChannel::factory()->create();

        // Create routes
        Route::create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $activeChannel1->id,
            'is_active' => true,
            'priority' => 100,
        ]);

        Route::create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $activeChannel2->id,
            'is_active' => true,
            'priority' => 200,
        ]);

        Route::create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $inactiveChannel->id,
            'is_active' => false,
            'priority' => 150,
        ]);

        $activeChannels = $feed->activeChannels;

        $this->assertCount(2, $activeChannels);
        $this->assertTrue($activeChannels->contains('id', $activeChannel1->id));
        $this->assertTrue($activeChannels->contains('id', $activeChannel2->id));
        $this->assertFalse($activeChannels->contains('id', $inactiveChannel->id));

        // Test ordering by priority descending
        $channelIds = $activeChannels->pluck('id')->toArray();
        $this->assertEquals($activeChannel2->id, $channelIds[0]); // Priority 200
        $this->assertEquals($activeChannel1->id, $channelIds[1]); // Priority 100
    }

    public function test_feed_creation_with_factory(): void
    {
        $feed = Feed::factory()->create();

        $this->assertInstanceOf(Feed::class, $feed);
        $this->assertIsString($feed->name);
        $this->assertIsString($feed->url);
        $this->assertIsString($feed->type);
        $this->assertNull($feed->language_id);
        $this->assertIsBool($feed->is_active);
        $this->assertIsArray($feed->settings);
    }

    public function test_feed_creation_with_explicit_values(): void
    {
        $language = Language::factory()->create();
        $settings = ['custom' => 'setting', 'nested' => ['key' => 'value']];

        $feed = Feed::create([
            'name' => 'Test Feed',
            'url' => 'https://example.com/feed',
            'type' => 'rss',
            'provider' => 'vrt',
            'language_id' => $language->id,
            'description' => 'Test description',
            'settings' => $settings,
            'is_active' => false,
        ]);

        $this->assertEquals('Test Feed', $feed->name);
        $this->assertEquals('https://example.com/feed', $feed->url);
        $this->assertEquals('rss', $feed->type);
        $this->assertEquals($language->id, $feed->language_id);
        $this->assertEquals('Test description', $feed->description);
        $this->assertEquals($settings, $feed->settings);
        $this->assertFalse($feed->is_active);
    }

    public function test_feed_update(): void
    {
        $feed = Feed::factory()->create([
            'name' => 'Original Name',
            'is_active' => true,
        ]);

        $feed->update([
            'name' => 'Updated Name',
            'is_active' => false,
        ]);

        $feed->refresh();

        $this->assertEquals('Updated Name', $feed->name);
        $this->assertFalse($feed->is_active);
    }

    public function test_feed_deletion(): void
    {
        $feed = Feed::factory()->create();
        $feedId = $feed->id;

        $feed->delete();

        $this->assertDatabaseMissing('feeds', ['id' => $feedId]);
    }

    public function test_feed_settings_can_be_empty_array(): void
    {
        $feed = Feed::factory()->create(['settings' => []]);

        $this->assertIsArray($feed->settings);
        $this->assertEmpty($feed->settings);
    }

    public function test_feed_settings_can_be_complex_structure(): void
    {
        $complexSettings = [
            'parsing' => [
                'selector' => 'article.post',
                'title_selector' => 'h1',
                'content_selector' => '.content',
            ],
            'filters' => ['min_length' => 100],
            'schedule' => [
                'enabled' => true,
                'interval' => 3600,
            ],
        ];

        $feed = Feed::factory()->create(['settings' => $complexSettings]);

        $this->assertEquals($complexSettings, $feed->settings);
        $this->assertEquals('article.post', $feed->settings['parsing']['selector']);
        $this->assertTrue($feed->settings['schedule']['enabled']);
    }

    public function test_scope_stale_includes_active_feeds_with_old_last_fetched_at(): void
    {
        $staleFeed = Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => now()->subHours(50),
        ]);

        $staleFeeds = Feed::stale(48)->get();

        $this->assertCount(1, $staleFeeds);
        $this->assertTrue($staleFeeds->contains('id', $staleFeed->id));
    }

    public function test_scope_stale_includes_active_feeds_never_fetched(): void
    {
        $neverFetchedFeed = Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => null,
        ]);

        $staleFeeds = Feed::stale(48)->get();

        $this->assertCount(1, $staleFeeds);
        $this->assertTrue($staleFeeds->contains('id', $neverFetchedFeed->id));
    }

    public function test_scope_stale_excludes_fresh_feeds(): void
    {
        Feed::factory()->create([
            'is_active' => true,
            'last_fetched_at' => now()->subHours(10),
        ]);

        $staleFeeds = Feed::stale(48)->get();

        $this->assertCount(0, $staleFeeds);
    }

    public function test_scope_stale_excludes_inactive_feeds(): void
    {
        Feed::factory()->create([
            'is_active' => false,
            'last_fetched_at' => now()->subHours(100),
        ]);

        $staleFeeds = Feed::stale(48)->get();

        $this->assertCount(0, $staleFeeds);
    }

    public function test_feed_can_have_null_last_fetched_at(): void
    {
        $feed = Feed::factory()->create(['last_fetched_at' => null]);

        $this->assertNull($feed->last_fetched_at);
    }

    public function test_feed_timestamps(): void
    {
        $feed = Feed::factory()->create();

        $this->assertNotNull($feed->created_at);
        $this->assertNotNull($feed->updated_at);
        $this->assertInstanceOf(Carbon::class, $feed->created_at);
        $this->assertInstanceOf(Carbon::class, $feed->updated_at);
    }
}
