<?php

namespace Tests\Unit\Actions;

use App\Actions\CreateRouteArticlesAction;
use App\Actions\FetchArticleDataAction;
use App\Actions\ValidateArticleAction;
use App\Enums\ApprovalStatusEnum;
use App\Models\Article;
use App\Models\Feed;
use App\Models\Keyword;
use App\Models\PlatformChannel;
use App\Models\Route;
use App\Models\RouteArticle;
use App\Models\Setting;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Mockery\MockInterface;
use Tests\TestCase;

class ValidateArticleActionTest extends TestCase
{
    use RefreshDatabase;

    private ValidateArticleAction $validateArticle;

    private MockInterface $articleFetcher;

    protected function setUp(): void
    {
        parent::setUp();
        $this->articleFetcher = Mockery::mock(FetchArticleDataAction::class);
        $this->validateArticle = new ValidateArticleAction($this->articleFetcher, new CreateRouteArticlesAction);
    }

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

    private function mockFetchReturning(Article $article, ?string $content, ?string $title = 'Test Title', ?string $description = 'Test description', ?string $thumbnail = null): void
    {
        $data = [];
        if ($title) {
            $data['title'] = $title;
        }
        if ($description) {
            $data['description'] = $description;
        }
        if ($content) {
            $data['full_article'] = $content;
        }
        if ($thumbnail) {
            $data['thumbnail'] = $thumbnail;
        }

        $this->articleFetcher
            ->shouldReceive('execute')
            ->with($article)
            ->once()
            ->andReturn($data);
    }

    public function test_validate_sets_validated_at_on_article(): void
    {
        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium');

        $this->validateArticle->execute($article);

        $this->assertNotNull($article->fresh()->validated_at);
    }

    public function test_validate_creates_route_articles_for_active_routes(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Some article content');

        $this->validateArticle->execute($article);

        $this->assertCount(2, RouteArticle::where('article_id', $article->id)->get());
    }

    public function test_validate_skips_inactive_routes(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);
        Route::factory()->inactive()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Some article content');

        $this->validateArticle->execute($article);

        $this->assertCount(1, RouteArticle::where('article_id', $article->id)->get());
    }

    public function test_validate_sets_pending_when_keywords_match(): void
    {
        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium politics');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
    }

    public function test_validate_sets_rejected_when_no_keywords_match(): void
    {
        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about random topics and weather');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::REJECTED, $routeArticle->approval_status);
    }

    public function test_validate_sets_pending_when_route_has_no_keywords(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about random topics');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
    }

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

        Route::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel1->id,
        ]);
        Route::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel2->id,
        ]);

        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel1->id,
            'keyword' => 'Belgium',
        ]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel2->id,
            'keyword' => 'Technology',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium');

        $this->validateArticle->execute($article);

        $ra1 = RouteArticle::where('article_id', $article->id)
            ->where('platform_channel_id', $channel1->id)->first();
        $ra2 = RouteArticle::where('article_id', $article->id)
            ->where('platform_channel_id', $channel2->id)->first();

        $this->assertEquals(ApprovalStatusEnum::PENDING, $ra1->approval_status);
        $this->assertEquals(ApprovalStatusEnum::REJECTED, $ra2->approval_status);
    }

    public function test_validate_auto_approves_when_global_setting_off_and_keywords_match(): void
    {
        Setting::setBool('enable_publishing_approvals', false);

        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::APPROVED, $routeArticle->approval_status);
    }

    public function test_validate_route_auto_approve_overrides_global_setting(): void
    {
        Setting::setBool('enable_publishing_approvals', true);

        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create([
            'feed_id' => $feed->id,
            'auto_approve' => true,
        ]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::APPROVED, $routeArticle->approval_status);
    }

    public function test_validate_route_auto_approve_false_overrides_global_off(): void
    {
        Setting::setBool('enable_publishing_approvals', false);

        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create([
            'feed_id' => $feed->id,
            'auto_approve' => false,
        ]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
    }

    public function test_validate_does_not_auto_approve_rejected_articles(): void
    {
        Setting::setBool('enable_publishing_approvals', false);

        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Random content no match');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::REJECTED, $routeArticle->approval_status);
    }

    public function test_validate_creates_no_route_articles_when_content_fetch_fails(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, null);

        $this->validateArticle->execute($article);

        $this->assertCount(0, RouteArticle::where('article_id', $article->id)->get());
        $this->assertNotNull($article->fresh()->validated_at);
    }

    public function test_validate_updates_article_metadata(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create([
            'feed_id' => $feed->id,
            'title' => 'Old Title',
        ]);
        $this->mockFetchReturning($article, 'Content about Belgium', 'New Title', 'New description');

        $result = $this->validateArticle->execute($article);

        $this->assertEquals('New Title', $result->title);
        $this->assertEquals('New description', $result->description);
        $this->assertEquals('Content about Belgium', $result->content);
    }

    public function test_validate_sets_validated_at_on_route_articles(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Content about something');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertNotNull($routeArticle->validated_at);
    }

    public function test_validate_keyword_matching_is_case_insensitive(): void
    {
        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about BELGIUM politics');

        $this->validateArticle->execute($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
    }

    public function test_validate_only_uses_active_keywords(): void
    {
        $feed = Feed::factory()->create();
        /** @var Route $route */
        $route = Route::factory()->active()->create(['feed_id' => $feed->id]);
        Keyword::factory()->inactive()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $route->platform_channel_id,
            'keyword' => 'Belgium',
        ]);

        $article = Article::factory()->create(['feed_id' => $feed->id]);
        $this->mockFetchReturning($article, 'Article about Belgium');

        $this->validateArticle->execute($article);

        // No active keywords = matches everything = pending
        $routeArticle = RouteArticle::where('article_id', $article->id)->first();
        $this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
    }

    public function test_validate_stores_thumbnail_in_image_url(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create([
            'feed_id' => $feed->id,
            'image_url' => null,
        ]);
        $this->mockFetchReturning($article, 'Content about Belgium', thumbnail: 'https://example.com/thumb.jpg');

        $this->validateArticle->execute($article);

        $this->assertEquals('https://example.com/thumb.jpg', $article->fresh()->image_url);
    }

    public function test_validate_stores_thumbnail_when_full_article_is_missing(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create([
            'feed_id' => $feed->id,
            'image_url' => null,
        ]);
        $this->mockFetchReturning($article, null, thumbnail: 'https://example.com/thumb.jpg');

        $this->validateArticle->execute($article);

        $this->assertEquals('https://example.com/thumb.jpg', $article->fresh()->image_url);
    }

    public function test_validate_leaves_image_url_null_when_parser_returns_no_thumbnail(): void
    {
        $feed = Feed::factory()->create();
        Route::factory()->active()->create(['feed_id' => $feed->id]);

        $article = Article::factory()->create([
            'feed_id' => $feed->id,
            'image_url' => null,
        ]);
        $this->mockFetchReturning($article, 'Content about Belgium');

        $this->validateArticle->execute($article);

        $this->assertNull($article->fresh()->image_url);
    }
}
