<?php

namespace Tests\Unit\Services;

use App\Models\Article;
use App\Models\Feed;
use App\Services\Article\ArticleFetcher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Mockery;
use Tests\TestCase;
use Tests\Traits\CreatesArticleFetcher;

class ArticleFetcherTest extends TestCase
{
    use CreatesArticleFetcher, RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        // Mock all HTTP requests by default to prevent external calls
        Http::fake([
            '*' => Http::response('<html><body>Mock HTML content</body></html>', 200),
        ]);

        // Create ArticleFetcher only when needed - tests will create their own
    }

    public function test_get_articles_from_feed_returns_collection(): void
    {
        $articleFetcher = $this->createArticleFetcher();

        $feed = Feed::factory()->create([
            'type' => 'rss',
            'url' => 'https://example.com/feed.rss',
        ]);

        $result = $articleFetcher->getArticlesFromFeed($feed);

        $this->assertInstanceOf(Collection::class, $result);
    }

    public function test_get_articles_from_rss_feed_returns_empty_collection(): void
    {
        $feed = Feed::factory()->create([
            'type' => 'rss',
            'url' => 'https://example.com/feed.rss',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->getArticlesFromFeed($feed);

        // RSS parsing is not implemented yet, should return empty collection
        $this->assertEmpty($result);
    }

    public function test_get_articles_from_website_feed_handles_no_parser(): void
    {
        $feed = Feed::factory()->create([
            'type' => 'website',
            'url' => 'https://unsupported-site.com/',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->getArticlesFromFeed($feed);

        // Should return empty collection when no parser is available
        $this->assertInstanceOf(Collection::class, $result);
        $this->assertEmpty($result);
    }

    public function test_get_articles_from_unsupported_feed_type(): void
    {
        $feed = Feed::factory()->create([
            'type' => 'website', // Use valid type but with unsupported URL
            'url' => 'https://unsupported-feed-type.com/feed',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->getArticlesFromFeed($feed);

        $this->assertInstanceOf(Collection::class, $result);
        $this->assertEmpty($result);
    }

    public function test_fetch_article_data_returns_array(): void
    {
        $article = Article::factory()->create([
            'url' => 'https://example.com/article',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->fetchArticleData($article);

        $this->assertIsArray($result);
        // Will be empty array due to unsupported URL in test
        $this->assertEmpty($result);
    }

    public function test_fetch_article_data_handles_invalid_url(): void
    {
        $article = Article::factory()->create([
            'url' => 'invalid-url',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->fetchArticleData($article);

        $this->assertIsArray($result);
        $this->assertEmpty($result);
    }

    public function test_get_articles_from_feed_with_null_feed_type(): void
    {
        // Create feed with valid type first, then manually set to invalid value
        $feed = Feed::factory()->create([
            'type' => 'website',
            'url' => 'https://example.com/feed',
        ]);

        // Use reflection to set an invalid type that bypasses enum validation
        $reflection = new \ReflectionClass($feed);
        $property = $reflection->getProperty('attributes');
        $property->setAccessible(true);
        $attributes = $property->getValue($feed);
        $attributes['type'] = 'invalid_type';
        $property->setValue($feed, $attributes);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->getArticlesFromFeed($feed);

        $this->assertInstanceOf(Collection::class, $result);
        $this->assertEmpty($result);
    }

    public function test_get_articles_from_website_feed_with_supported_parser(): void
    {
        // Mock successful HTTP response with sample HTML
        Http::fake([
            'https://www.vrt.be/vrtnws/nl/' => Http::response('<html><body>Sample VRT content</body></html>', 200),
        ]);

        $feed = Feed::factory()->create([
            'type' => 'website',
            'url' => 'https://www.vrt.be/vrtnws/nl/',
        ]);

        // Test actual behavior - VRT parser should be available
        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->getArticlesFromFeed($feed);

        $this->assertInstanceOf(Collection::class, $result);
        // VRT parser will process the mocked HTML response
    }

    public function test_get_articles_from_website_feed_handles_invalid_url(): void
    {
        // HTTP mock already set in setUp() to return mock HTML for all requests

        $feed = Feed::factory()->create([
            'type' => 'website',
            'url' => 'https://invalid-domain-that-does-not-exist-12345.com/',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->getArticlesFromFeed($feed);

        $this->assertInstanceOf(Collection::class, $result);
        $this->assertEmpty($result);
    }

    public function test_fetch_article_data_with_supported_parser(): void
    {
        // Mock successful HTTP response with sample HTML
        Http::fake([
            'https://www.vrt.be/vrtnws/nl/test-article' => Http::response('<html><body>Sample article content</body></html>', 200),
        ]);

        $article = Article::factory()->create([
            'url' => 'https://www.vrt.be/vrtnws/nl/test-article',
        ]);

        // Test actual behavior - VRT parser should be available
        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->fetchArticleData($article);

        $this->assertIsArray($result);
        // VRT parser will process the mocked HTML response
    }

    public function test_fetch_article_data_handles_unsupported_domain(): void
    {
        $article = Article::factory()->create([
            'url' => 'https://unsupported-domain.com/article',
        ]);

        $articleFetcher = $this->createArticleFetcher();
        $result = $articleFetcher->fetchArticleData($article);

        $this->assertIsArray($result);
        $this->assertEmpty($result);
    }

    public function test_save_article_creates_new_article_when_not_exists(): void
    {
        $feed = Feed::factory()->create();
        $url = 'https://example.com/unique-article';

        // Ensure article doesn't exist
        $this->assertDatabaseMissing('articles', ['url' => $url]);

        $articleFetcher = $this->createArticleFetcher();
        // Use reflection to access private method for testing
        $reflection = new \ReflectionClass($articleFetcher);
        $saveArticleMethod = $reflection->getMethod('saveArticle');
        $saveArticleMethod->setAccessible(true);

        $article = $saveArticleMethod->invoke($articleFetcher, $url, $feed->id);

        $this->assertInstanceOf(Article::class, $article);
        $this->assertEquals($url, $article->url);
        $this->assertEquals($feed->id, $article->feed_id);
        $this->assertDatabaseHas('articles', ['url' => $url, 'feed_id' => $feed->id]);
    }

    public function test_save_article_returns_existing_article_when_exists(): void
    {
        $feed = Feed::factory()->create();
        $existingArticle = Article::factory()->create([
            'url' => 'https://example.com/existing-article',
            'feed_id' => $feed->id,
        ]);

        // Use reflection to access private method for testing
        $reflection = new \ReflectionClass(ArticleFetcher::class);
        $saveArticleMethod = $reflection->getMethod('saveArticle');
        $saveArticleMethod->setAccessible(true);

        $articleFetcher = $this->createArticleFetcher();
        $article = $saveArticleMethod->invoke($articleFetcher, $existingArticle->url, $feed->id);

        $this->assertEquals($existingArticle->id, $article->id);
        $this->assertEquals($existingArticle->url, $article->url);

        // Ensure no duplicate was created
        $this->assertEquals(1, Article::where('url', $existingArticle->url)->count());
    }

    public function test_save_article_without_feed_id(): void
    {
        $url = 'https://example.com/article-without-feed';

        // Use reflection to access private method for testing
        $reflection = new \ReflectionClass(ArticleFetcher::class);
        $saveArticleMethod = $reflection->getMethod('saveArticle');
        $saveArticleMethod->setAccessible(true);

        $articleFetcher = $this->createArticleFetcher();
        $article = $saveArticleMethod->invoke($articleFetcher, $url, null);

        $this->assertInstanceOf(Article::class, $article);
        $this->assertEquals($url, $article->url);
        $this->assertNull($article->feed_id);
        $this->assertDatabaseHas('articles', ['url' => $url, 'feed_id' => null]);
    }

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
}
