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 PostDraft, Provider, UploadRequest, VideoAsset, VideoStatus


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


def make_youtube_draft(engine, tmp_path, *, description="Original description", tags=None):
    video_file = tmp_path / "video.mp4"
    video_file.write_bytes(b"production video bytes")
    checksum = "09bd52458df5d04216fd32ece7cddac4e4c962be9ffea1f4ac6e6bbe25ab0bdb"
    with Session(engine) as session:
        video = VideoAsset(
            candidate_id="package_1a52f20e_v2",
            package_id="package_1a52f20e_v2",
            source="prepared_package",
            file_path=str(video_file),
            file_size=video_file.stat().st_size,
            checksum=checksum,
            working_title="Delivery bot card trap",
            status=VideoStatus.ready_for_review,
            is_aigc=True,
        )
        session.add(video)
        session.commit()
        session.refresh(video)
        draft = PostDraft(
            video_asset_id=video.id,
            provider=Provider.youtube,
            title="Old title",
            description=description,
            tags_json=tags or ["Online Safety", "Scam Alert"],
            privacy_status="private",
            category_id="22",
            metadata_json={"disclosure": {"synthetic_media": True}},
        )
        session.add(draft)
        session.commit()
        session.refresh(draft)
        return video.id, draft.id


def test_build_youtube_description_appends_normalized_hashtags_once(tmp_path):
    _, client = setup_test_db(tmp_path)
    body = client.post("/api/upload-requests/youtube-description-preview", json={
        "description": "Changed description\n\n#scamalert",
        "tags": ["Scam Alert", "Online Safety", "cybersecurity", "bad tag!"],
    })
    assert body.status_code == 200
    description = body.json()["description"]
    assert description.startswith("Changed description")
    assert description.count("#scamalert") == 1
    assert "#onlinesafety" in description
    assert "#cybersecurity" in description
    assert "bad tag!" not in description


def test_save_before_approve_snapshot_uses_final_description_and_hashtags(tmp_path):
    engine, client = setup_test_db(tmp_path)
    _video_id, draft_id = make_youtube_draft(engine, tmp_path)

    response = client.post(f"/api/upload-requests/youtube-private/{draft_id}/save-and-approve", json={
        "draft": {
            "title": "Final creator title",
            "description": "Final creator description from the review UI.",
            "tags_json": ["Scam Alert", "Online Safety", "Card Fraud"],
            "privacy_status": "private",
            "metadata_json": {"disclosure": {"synthetic_media": True}},
        },
        "approved_by": "sir",
    })
    assert response.status_code == 200
    snapshot = response.json()["upload_snapshot"]
    assert snapshot["youtube_title"] == "Final creator title"
    assert snapshot["youtube_description_source"] == "Final creator description from the review UI."
    assert snapshot["youtube_description_final"].startswith("Final creator description from the review UI.")
    assert "#scamalert" in snapshot["youtube_description_final"]
    assert "#onlinesafety" in snapshot["youtube_description_final"]
    assert "#cardfraud" in snapshot["youtube_description_final"]
    assert snapshot["privacy_status"] == "private"
    assert snapshot["synthetic_media"] is True
    assert snapshot["approved_by"] == "sir"

    with Session(engine) as session:
        draft = session.get(PostDraft, draft_id)
        request = session.exec(select(UploadRequest)).one()
        assert draft.title == "Final creator title"
        assert draft.description == "Final creator description from the review UI."
        assert request.metadata_snapshot_json["youtube_description_final"] == snapshot["youtube_description_final"]
        assert "refresh" not in Path(request.outbox_path).read_text().lower()
        assert "token" not in Path(request.outbox_path).read_text().lower()


def test_metadata_change_after_approval_invalidates_open_upload_request(tmp_path):
    engine, client = setup_test_db(tmp_path)
    _video_id, draft_id = make_youtube_draft(engine, tmp_path)
    approved = client.post(f"/api/upload-requests/youtube-private/{draft_id}/save-and-approve", json={"approved_by": "sir"})
    assert approved.status_code == 200
    request_id = approved.json()["id"]

    changed = client.patch(f"/api/post-drafts/{draft_id}", json={"description": "Changed after approval"})
    assert changed.status_code == 200

    with Session(engine) as session:
        request = session.get(UploadRequest, request_id)
        assert request.status == "invalidated"
        assert "metadata changed" in (request.error or "")


def test_execute_existing_jarvis_upload_dry_run_uses_snapshot_not_live_draft(tmp_path):
    engine, client = setup_test_db(tmp_path)
    _video_id, draft_id = make_youtube_draft(engine, tmp_path)
    approved = client.post(f"/api/upload-requests/youtube-private/{draft_id}/save-and-approve", json={
        "draft": {"description": "Snapshot description", "tags_json": ["Snapshot Tag"]},
        "approved_by": "sir",
    })
    assert approved.status_code == 200
    request_id = approved.json()["id"]
    # Direct DB mutation simulates a stale/live draft changing after approval without using the API invalidation path.
    with Session(engine) as session:
        draft = session.get(PostDraft, draft_id)
        draft.description = "Live draft drift that must not upload"
        session.add(draft)
        session.commit()

    executed = client.post(f"/api/upload-requests/{request_id}/execute-existing-jarvis", json={"dry_run": True})
    assert executed.status_code == 200
    audit = executed.json()["audit"]
    request_json = str(audit["request_redacted_json"])
    assert "Snapshot description" in request_json
    assert "#snapshottag" in request_json
    assert "Live draft drift" not in request_json
    assert executed.json()["youtube_video_id"].startswith("dryrun_")
