import json
from io import StringIO

from autoshorts.claims.fact_gate import Claim, ClaimType
from autoshorts.cli.demo_pilot_production_package_v2 import build_demo_pilot_package_v2, main
from autoshorts.compliance.ai_disclosure import AIDisclosureReview, DisclosureValue, SyntheticMediaType
from autoshorts.production.pilot_package_v2 import (
    PilotSelection,
    build_pilot_production_package,
    select_pilot_candidates,
)
from autoshorts.production.resource_gate import (
    MediaGenerationGate,
    RenderApprovalState,
    ResourceCostTier,
    evaluate_media_generation_gate,
)
from autoshorts.strategy.spine_v2 import AIDisclosureDecision, build_demo_candidates_v2, build_review_package_v2


def pilot_candidate():
    return build_demo_candidates_v2(limit=1)[0]


def pilot_package():
    candidate = pilot_candidate()
    review = build_review_package_v2(candidate)
    return build_pilot_production_package(candidate, review)


def test_resource_cost_tier_and_render_state_values_exist():
    assert ResourceCostTier.TEXT_REASONING.value == "text_reasoning"
    assert ResourceCostTier.WAN_FINAL_RENDER.value == "wan_final_render"
    assert ResourceCostTier.PUBLISHING.value == "publishing"
    assert RenderApprovalState.APPROVED_FOR_FINAL_RENDER.value == "approved_for_final_render"


def test_media_generation_gate_allows_text_and_preview_but_blocks_wan_without_final_approval():
    candidate = pilot_candidate()
    review = build_review_package_v2(candidate)

    text_gate = evaluate_media_generation_gate(
        candidate=candidate,
        review_package=review,
        requested_resource_tier=ResourceCostTier.TEXT_REASONING,
        approval_state=RenderApprovalState.DRAFT,
        approved_candidate_id=candidate.candidate_id,
        approved_version=candidate.version,
        approved_script_hash=review.script_hash,
    )
    assert text_gate.allowed

    preview_gate = evaluate_media_generation_gate(
        candidate=candidate,
        review_package=review,
        requested_resource_tier=ResourceCostTier.DETERMINISTIC_PREVIEW,
        approval_state=RenderApprovalState.READY_FOR_REVIEW,
        approved_candidate_id=candidate.candidate_id,
        approved_version=candidate.version,
        approved_script_hash=review.script_hash,
    )
    assert preview_gate.allowed

    wan_gate = evaluate_media_generation_gate(
        candidate=candidate,
        review_package=review,
        requested_resource_tier=ResourceCostTier.WAN_FINAL_RENDER,
        approval_state=RenderApprovalState.APPROVED_FOR_STYLEFRAME,
        approved_candidate_id=candidate.candidate_id,
        approved_version=candidate.version,
        approved_script_hash=review.script_hash,
    )
    assert not wan_gate.allowed
    assert "APPROVED_FOR_FINAL_RENDER" in wan_gate.reason


def test_publishing_is_always_blocked_and_wrong_hash_blocks_finalrender():
    candidate = pilot_candidate()
    review = build_review_package_v2(candidate)

    publish = evaluate_media_generation_gate(
        candidate=candidate,
        review_package=review,
        requested_resource_tier=ResourceCostTier.PUBLISHING,
        approval_state=RenderApprovalState.APPROVED_FOR_MANUAL_POSTING,
        approved_candidate_id=candidate.candidate_id,
        approved_version=candidate.version,
        approved_script_hash=review.script_hash,
    )
    assert not publish.allowed
    assert "Publishing" in publish.reason

    wrong_hash = evaluate_media_generation_gate(
        candidate=candidate,
        review_package=review,
        requested_resource_tier=ResourceCostTier.WAN_FINAL_RENDER,
        approval_state=RenderApprovalState.APPROVED_FOR_FINAL_RENDER,
        approved_candidate_id=candidate.candidate_id,
        approved_version=candidate.version,
        approved_script_hash="bad-hash",
    )
    assert not wrong_hash.allowed
    assert "script_hash" in wrong_hash.reason


def test_ai_disclosure_unclear_and_claim_gate_block_finalrender():
    candidate = pilot_candidate()
    review = build_review_package_v2(candidate)
    unclear = AIDisclosureReview(
        youtube=DisclosureValue.UNCLEAR,
        tiktok=DisclosureValue.NO,
        instagram=DisclosureValue.NO,
        linkedin=DisclosureValue.NO,
        reason="unclear synthetic media status",
        synthetic_media_type=SyntheticMediaType.UNCLEAR,
    )
    gate = evaluate_media_generation_gate(
        candidate=candidate,
        review_package=review,
        requested_resource_tier=ResourceCostTier.WAN_FINAL_RENDER,
        approval_state=RenderApprovalState.APPROVED_FOR_FINAL_RENDER,
        approved_candidate_id=candidate.candidate_id,
        approved_version=candidate.version,
        approved_script_hash=review.script_hash,
        ai_disclosure_review=unclear,
    )
    assert not gate.allowed
    assert "AI disclosure" in gate.reason

    bad_claim_candidate = build_demo_candidates_v2(limit=1)[0]
    bad_claim_candidate = bad_claim_candidate.__class__(
        **{**bad_claim_candidate.__dict__, "claims": (Claim("specific sourced statement", ClaimType.SOURCED_FACT),)}
    )
    bad_review = build_review_package_v2(bad_claim_candidate)
    claim_gate = evaluate_media_generation_gate(
        candidate=bad_claim_candidate,
        review_package=bad_review,
        requested_resource_tier=ResourceCostTier.WAN_FINAL_RENDER,
        approval_state=RenderApprovalState.APPROVED_FOR_FINAL_RENDER,
        approved_candidate_id=bad_claim_candidate.candidate_id,
        approved_version=bad_claim_candidate.version,
        approved_script_hash=bad_review.script_hash,
    )
    assert not claim_gate.allowed
    assert "Claim Gate" in claim_gate.reason


def test_pilot_package_is_deterministic_serializable_and_has_render_plans():
    package = pilot_package()
    first = package.to_json()
    second = package.to_json()
    data = json.loads(first)

    assert first == second
    assert data["candidate_id"] == "ai-output-autopsy-decision-summary-38s"
    assert data["approval_state"] == RenderApprovalState.READY_FOR_REVIEW.value
    assert data["voiceover_text"].startswith("Your AI summary is useless")
    assert data["wan_prompt_plan"][0]["required_overlay_text"] == []
    assert data["render_manifest_draft"]["final_render_enabled"] is False
    assert data["resource_gate_result"]["requested_resource_tier"] == ResourceCostTier.DETERMINISTIC_PREVIEW.value


def test_demo_selection_returns_one_pilot_one_backup_and_one_deferred():
    selection = select_pilot_candidates(build_demo_candidates_v2(limit=3))
    assert selection.main_pilot.candidate_id == "ai-output-autopsy-decision-summary-38s"
    assert selection.backup.candidate_id == "digital-red-flags-fake-invoice-35s"
    assert selection.deferred.candidate_id == "workflow-teardown-exception-path-40s"


def test_demo_cli_emits_max_one_pilot_one_backup_and_no_external_calls():
    out = StringIO()
    assert main(["--json"], stdout=out) == 0
    data = json.loads(out.getvalue())

    assert data["main_pilot"]["candidate_id"] == "ai-output-autopsy-decision-summary-38s"
    assert data["backup"]["candidate_id"] == "digital-red-flags-fake-invoice-35s"
    assert data["deferred"]["candidate_id"] == "workflow-teardown-exception-path-40s"
    assert data["side_effects"] == []
    assert data["external_calls"] == []
    assert data["main_pilot"]["render_manifest_draft"]["wan_calls_enabled"] is False
    assert data["main_pilot"]["render_manifest_draft"]["ai_image_generation_enabled"] is False
