<?php

namespace App\Tests\Blog;

use App\Blog\Entity\Blog;
use App\Tests\ApiTestCase;

/**
 * The medical-review gate: the content pipeline creates drafts as
 * review_status=pending_review; a doctor/admin approves (publishes) or rejects
 * them. review_status=null stays reserved for manual admin posts outside the gate.
 */
class BlogReviewGateTest extends ApiTestCase
{
    private function makePendingPost(string $title, ?string $topicSlug = null): Blog
    {
        $blog = new Blog($this->createUser(['ROLE_ADMIN']), $title, 'متن آزمایشی مقاله برای تست');
        $blog->setStatus(Blog::STATUS_DRAFT)->setReviewStatus(Blog::REVIEW_PENDING);
        if ($topicSlug !== null) {
            $blog->setTopicSlug($topicSlug);
        }
        $this->em->persist($blog);
        $this->em->flush();

        return $blog;
    }

    // ── review queue ──────────────────────────────────────────────────────────

    public function testReviewQueueReturnsOnlyPendingPosts(): void
    {
        $admin = $this->createUser(['ROLE_ADMIN']);
        $tag   = bin2hex(random_bytes(4));

        $this->makePendingPost("در-انتظار-$tag");
        $published = new Blog($admin, "منتشرشده-$tag", 'متن آزمایشی مقاله برای تست');
        $published->setStatus(Blog::STATUS_PUBLISHED); // review_status null → outside the gate
        $this->em->persist($published);
        $this->em->flush();

        $payload = $this->authJson('GET', '/api/v1/admin/blog/review-queue?limit=50', $admin);
        $titles  = array_column($payload['data'], 'title');

        $this->assertContains("در-انتظار-$tag", $titles, 'pending post must be in the queue');
        $this->assertNotContains("منتشرشده-$tag", $titles, 'non-pending post leaked into the queue');
    }

    public function testReviewQueueRequiresAdmin(): void
    {
        $user = $this->createUser(['ROLE_USER']);
        $this->authJson('GET', '/api/v1/admin/blog/review-queue', $user);
        $this->assertSame(403, $this->responseCode());
    }

    public function testReviewQueueIsReadableByImporter(): void
    {
        // پنل کرالر با کاربر سیستمیِ ROLE_IMPORTER لاگین می‌کند؛ بدون این، صف
        // همیشه «review-queue failed (403)» می‌داد.
        $importer = $this->createUser(['ROLE_IMPORTER']);
        $this->authJson('GET', '/api/v1/admin/blog/review-queue', $importer);
        $this->assertSame(200, $this->responseCode());
    }

    // ── who may create a draft ────────────────────────────────────────────────

    public function testImporterCreatesDraftForcedIntoTheReviewGate(): void
    {
        $importer = $this->createUser(['ROLE_IMPORTER']);

        $res = $this->authJson('POST', '/api/v1/blog', $importer, [
            'title'         => 'مقاله خط تولید ' . bin2hex(random_bytes(3)),
            'body'          => 'متن آزمایشی مقاله برای تست',
            'topic_slug'    => 'importer-' . bin2hex(random_bytes(4)),
            // تلاش برای انتشار مستقیم — باید نادیده گرفته شود
            'status'        => Blog::STATUS_PUBLISHED,
            'review_status' => Blog::REVIEW_APPROVED,
        ]);

        $this->assertSame(201, $this->responseCode());
        $this->assertSame(Blog::STATUS_DRAFT, $res['data']['data']['status'],
            'importer must never publish directly');
        $this->assertSame(Blog::REVIEW_PENDING, $res['data']['data']['review_status'],
            'importer draft must enter the doctor review gate');
    }

    public function testPlainUserCannotCreateBlog(): void
    {
        $user = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', '/api/v1/blog', $user, [
            'title' => 'عنوان', 'body' => 'متن آزمایشی مقاله برای تست',
        ]);
        $this->assertSame(403, $this->responseCode());
    }

    // ── approve ───────────────────────────────────────────────────────────────

    public function testApprovePublishesAndRecordsReviewer(): void
    {
        $doctor = $this->createUser(['ROLE_ADMIN']);
        $post   = $this->makePendingPost('مقاله ' . bin2hex(random_bytes(3)));

        $res = $this->authJson('POST', '/api/v1/admin/blog/' . $post->getUuid() . '/review', $doctor, [
            'decision' => 'approved',
        ]);
        $this->assertSame(200, $this->responseCode());

        $blog = $res['data']['data'];
        $this->assertSame(Blog::REVIEW_APPROVED, $blog['review_status']);
        $this->assertSame(Blog::STATUS_PUBLISHED, $blog['status'], 'approve must publish by default');
        $this->assertSame($doctor->getUuid(), $blog['reviewer']['uuid'], 'reviewer identity must be stored (E-E-A-T)');
        $this->assertNotNull($blog['reviewed_at']);
    }

    public function testApproveWithPublishFalseKeepsDraft(): void
    {
        $doctor = $this->createUser(['ROLE_ADMIN']);
        $post   = $this->makePendingPost('مقاله ' . bin2hex(random_bytes(3)));

        $res = $this->authJson('POST', '/api/v1/admin/blog/' . $post->getUuid() . '/review', $doctor, [
            'decision' => 'approved',
            'publish'  => false,
        ]);
        $this->assertSame(200, $this->responseCode());
        $this->assertSame(Blog::REVIEW_APPROVED, $res['data']['data']['review_status']);
        $this->assertSame(Blog::STATUS_DRAFT, $res['data']['data']['status']);
    }

    // ── reject ────────────────────────────────────────────────────────────────

    public function testRejectRequiresNoteAndKeepsDraft(): void
    {
        $doctor = $this->createUser(['ROLE_ADMIN']);
        $post   = $this->makePendingPost('مقاله ' . bin2hex(random_bytes(3)));

        // boundary: rejection without a note is refused
        $this->authJson('POST', '/api/v1/admin/blog/' . $post->getUuid() . '/review', $doctor, [
            'decision' => 'rejected',
        ]);
        $this->assertSame(422, $this->responseCode(), 'rejection must require a reason');

        $res = $this->authJson('POST', '/api/v1/admin/blog/' . $post->getUuid() . '/review', $doctor, [
            'decision' => 'rejected',
            'note'     => 'ادعاهای پزشکی بدون منبع کافی',
        ]);
        $this->assertSame(200, $this->responseCode());
        $this->assertSame(Blog::REVIEW_REJECTED, $res['data']['data']['review_status']);
        $this->assertSame(Blog::STATUS_DRAFT, $res['data']['data']['status'], 'rejected post must not be published');
        $this->assertSame('ادعاهای پزشکی بدون منبع کافی', $res['data']['data']['review_note']);
    }

    public function testInvalidDecisionIsRejected(): void
    {
        $doctor = $this->createUser(['ROLE_ADMIN']);
        $post   = $this->makePendingPost('مقاله ' . bin2hex(random_bytes(3)));

        $this->authJson('POST', '/api/v1/admin/blog/' . $post->getUuid() . '/review', $doctor, [
            'decision' => 'maybe',
        ]);
        $this->assertSame(422, $this->responseCode());
    }

    public function testReviewUnknownPostReturns404(): void
    {
        $doctor = $this->createUser(['ROLE_ADMIN']);
        $this->authJson('POST', '/api/v1/admin/blog/00000000-0000-0000-0000-000000000000/review', $doctor, [
            'decision' => 'approved',
        ]);
        $this->assertSame(404, $this->responseCode());
    }

    // ── pipeline idempotency ──────────────────────────────────────────────────

    public function testCreateIsIdempotentOnTopicSlug(): void
    {
        $admin = $this->createUser(['ROLE_ADMIN']);
        $slug  = 'cardiology-chest-pain-' . bin2hex(random_bytes(3));

        $first = $this->authJson('POST', '/api/v1/blog', $admin, [
            'title'         => 'درد قفسه سینه',
            'body'          => 'متن آزمایشی مقاله برای تست',
            'topic_slug'    => $slug,
            'sources'       => [['url' => 'https://mayoclinic.org/a', 'title' => 'Mayo']],
            'review_status' => 'pending_review',
        ]);
        $this->assertSame(201, $this->responseCode());
        $this->assertSame('pending_review', $first['data']['data']['review_status']);
        $firstUuid = $first['data']['data']['uuid'];

        // A re-run with the same topic_slug must return the SAME post, not a duplicate.
        $second = $this->authJson('POST', '/api/v1/blog', $admin, [
            'title'      => 'درد قفسه سینه (دوباره)',
            'body'       => 'متن دیگر',
            'topic_slug' => $slug,
        ]);
        $this->assertSame(200, $this->responseCode(), 're-run must be idempotent, not 201');
        $this->assertSame($firstUuid, $second['data']['data']['uuid'], 'topic_slug must not create a duplicate');
    }
}
