from pathlib import Path

from fastapi.testclient import TestClient
from sqlmodel import Session, select

from app.core.config import get_settings
from app.db.session import init_db, reset_db_for_tests
from app.main import app
from app.models.core import ActivityEvent, PostDraft, ProductionQueueItem, Provider, RevisionRequest, ReviewDecision, VideoAsset
from app.services.importers.prepared_package import PreparedPackageImporter
from app.services.upload_requests import create_existing_upload_request


def setup_test_db(tmp_path):
    settings = get_settings()
    settings.database_url = "sqlite://"
    settings.storage_root = tmp_path / "storage"
    engine = init_db(settings)
    reset_db_for_tests(engine)
    return engine, TestClient(app)


def base_manifest(item_id: str, package_id: str = "package_quality_test") -> dict:
    return {
        "package_id": package_id,
        "source": "prepared_package",
        "queue_item_id": item_id,
        "working_title": "Quality gated package",
        "language": "en",
        "series": "Everyday Red Flags",
        "suggested_tiktok_caption": "Caption",
        "suggested_youtube_title": "Quality gated package",
        "suggested_youtube_description": "Description",
        "suggested_hashtags": ["#onlinesafety"],
        "script": "If the chatbot asks for a card, stop.",
        "disclosure": {"synthetic_media": True, "commercial_content": False},
        "status": "ready_for_review",
    }


def test_reject_sets_video_drafts_and_queue_out_of_active_review(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video = VideoAsset(candidate_id="bad-video", package_id="package_bad", source="prepared_package", file_path="", checksum="badchecksum", working_title="Bad", status="ready_for_review")
        session.add(video); session.commit(); session.refresh(video)
        session.add(PostDraft(video_asset_id=video.id, provider=Provider.youtube, title="Bad", description="desc", privacy_status="private", status="draft"))
        session.add(PostDraft(video_asset_id=video.id, provider=Provider.tiktok, caption="cap", status="draft"))
        queue = ProductionQueueItem(title="Bad queue", status="in_review", source="manual", video_asset_id=video.id, actual_package_id="package_bad")
        session.add(queue); session.commit(); session.refresh(queue)
        video_id = video.id
    result = client.post(f"/api/videos/{video_id}/reject", json={"reason": "Visual quality below standard", "categories": ["Visual quality", "Voice-over"]}).json()
    assert result["video"]["status"] == "rejected"
    with Session(engine) as session:
        video = session.get(VideoAsset, video_id)
        drafts = session.exec(select(PostDraft).where(PostDraft.video_asset_id == video_id)).all()
        queue = session.exec(select(ProductionQueueItem).where(ProductionQueueItem.video_asset_id == video_id)).first()
        decision = session.exec(select(ReviewDecision).where(ReviewDecision.video_asset_id == video_id)).first()
        assert video.status == "rejected"
        assert all(d.status == "rejected" for d in drafts)
        assert queue.status == "needs_changes"
        assert decision.decision == "rejected"
    ready = client.get("/api/videos?status=ready_for_review").json()["items"]
    assert all(v["id"] != video_id for v in ready)


def test_request_changes_creates_revision_and_sets_queue_needs_changes(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video = VideoAsset(candidate_id="needs-change-video", package_id="package_change", source="prepared_package", file_path="", checksum="changechecksum", working_title="Needs Change", status="ready_for_review")
        session.add(video); session.commit(); session.refresh(video)
        queue = ProductionQueueItem(title="Needs change queue", status="in_review", source="manual", video_asset_id=video.id, actual_package_id="package_change")
        session.add(queue); session.commit(); session.refresh(queue)
        video_id = video.id
    result = client.post(f"/api/videos/{video_id}/request-changes", json={"requested_changes": "Regenerate premium AI styleframes", "categories": ["Visual quality"]}).json()
    assert result["video"]["status"] == "needs_changes"
    with Session(engine) as session:
        revision = session.exec(select(RevisionRequest).where(RevisionRequest.video_asset_id == video_id)).first()
        decision = session.exec(select(ReviewDecision).where(ReviewDecision.video_asset_id == video_id)).first()
        queue = session.exec(select(ProductionQueueItem).where(ProductionQueueItem.video_asset_id == video_id)).first()
        assert revision.status == "open"
        assert revision.requested_changes == "Regenerate premium AI styleframes"
        assert decision.decision == "needs_changes"
        assert queue.status == "needs_changes"


def test_import_queue_package_without_quality_report_is_not_ready_for_review(tmp_path):
    engine, _ = setup_test_db(tmp_path)
    source_video = tmp_path / "input.mp4"
    source_video.write_bytes(b"fake video bytes")
    with Session(engine) as session:
        item = ProductionQueueItem(title="Quality missing", status="producing", source="manual", expected_package_id="package_missing_quality")
        session.add(item); session.commit(); session.refresh(item)
        manifest = base_manifest(item.id, "package_missing_quality")
    video_id = PreparedPackageImporter(settings=get_settings(), engine=engine).import_manifest_payload(manifest, source_video)
    with Session(engine) as session:
        video = session.get(VideoAsset, video_id)
        item = session.get(ProductionQueueItem, item.id)
        event = session.exec(select(ActivityEvent).where(ActivityEvent.event_type == "production_quality_failed")).first()
        assert video.status == "needs_internal_fix"
        assert item.status == "needs_internal_fix"
        assert event is not None


def test_import_queue_package_with_quality_passed_is_ready_for_review(tmp_path):
    engine, _ = setup_test_db(tmp_path)
    source_video = tmp_path / "input.mp4"
    source_video.write_bytes(b"fake video bytes passed")
    with Session(engine) as session:
        item = ProductionQueueItem(title="Quality passed", status="producing", source="manual", expected_package_id="package_quality_passed")
        session.add(item); session.commit(); session.refresh(item)
        manifest = base_manifest(item.id, "package_quality_passed")
        manifest["quality"] = {
            "visual_gate": "passed",
            "voice_gate": "passed",
            "hook_gate": "passed",
            "metadata_gate": "passed",
            "safety_gate": "passed",
            "package_integrity_gate": "passed",
            "overall": "passed",
        }
        manifest["voice_qa"] = {"voice_engine": "Chatterbox", "transcript_match": "passed", "wer_estimate": 0.01}
        manifest["visual_qa"] = {"styleframes": 4, "powerpoint_look": False}
    video_id = PreparedPackageImporter(settings=get_settings(), engine=engine).import_manifest_payload(manifest, source_video)
    with Session(engine) as session:
        video = session.get(VideoAsset, video_id)
        event = session.exec(select(ActivityEvent).where(ActivityEvent.entity_id == video_id, ActivityEvent.event_type == "quality_report_imported")).first()
        assert video.status == "ready_for_review"
        assert event.payload_json["overall"] == "passed"


def test_import_queue_package_with_forbidden_visual_overlay_fails_quality(tmp_path):
    engine, _ = setup_test_db(tmp_path)
    source_video = tmp_path / "input.mp4"
    source_video.write_bytes(b"fake video bytes visual fail")
    with Session(engine) as session:
        item = ProductionQueueItem(title="Visual fail", status="producing", source="manual", expected_package_id="package_visual_fail")
        session.add(item); session.commit(); session.refresh(item)
        manifest = base_manifest(item.id, "package_visual_fail")
        manifest["quality"] = {
            "visual_gate": "passed",
            "voice_gate": "passed",
            "hook_gate": "passed",
            "metadata_gate": "passed",
            "safety_gate": "passed",
            "package_integrity_gate": "passed",
            "overall": "passed",
            "checks": {
                "premium_ai_styleframes_used": True,
                "styleframes_text_free": True,
                "no_large_top_title": False,
                "no_unnecessary_lower_third_dim": True,
                "no_powerpoint_layout": True,
                "renderer_owned_text_listed": True,
            },
        }
        manifest["visual_qa"] = {"renderer_owned_text": ["small subtitle"], "large_top_title_detected": True}
    video_id = PreparedPackageImporter(settings=get_settings(), engine=engine).import_manifest_payload(manifest, source_video)
    with Session(engine) as session:
        video = session.get(VideoAsset, video_id)
        event = session.exec(select(ActivityEvent).where(ActivityEvent.entity_id == video_id, ActivityEvent.event_type == "quality_report_imported")).first()
        assert video.status == "needs_internal_fix"
        assert event.payload_json["gates"]["visual_gate"] == "failed"
        assert event.payload_json["overall"] == "failed"
        assert event.payload_json["gates"]["checks"]["no_large_top_title"] is False


def test_rejected_video_cannot_create_upload_request(tmp_path):
    engine, _ = setup_test_db(tmp_path)
    with Session(engine) as session:
        video = VideoAsset(candidate_id="rejected-upload", package_id="package_rejected", source="prepared_package", file_path="", checksum="rejectuploadchecksum", working_title="Rejected", status="rejected")
        session.add(video); session.commit(); session.refresh(video)
        draft = PostDraft(video_asset_id=video.id, provider=Provider.youtube, title="Rejected", description="desc", privacy_status="private")
        session.add(draft); session.commit(); session.refresh(draft)
        try:
            create_existing_upload_request(session, draft.id)
            assert False, "expected blocked upload request"
        except Exception as exc:
            assert getattr(exc, "status_code", None) == 409


def test_agent_next_delivers_quality_profile_and_director_skill(tmp_path):
    _, client = setup_test_db(tmp_path)
    item = client.post("/api/production-queue", json={"title": "Next", "status": "locked_next", "source": "manual"}).json()
    nxt = client.get("/api/agent/production/next").json()
    assert nxt["queue_item_id"] == item["id"]
    assert nxt["quality_profile"]["version"] == "autoshorts-quality-v1.1"
    assert "visual_policy" in nxt["quality_profile"]
    assert "no_large_top_title" in nxt["quality_profile"]["visual_checks_required"]
    assert nxt["director_skill"] == {"name": "AutoShorts Director Quality v1", "doc": "docs/director-production-skill.md", "required": True}
