import json
import subprocess
from datetime import datetime, timezone
from pathlib import Path

from fastapi.testclient import TestClient
from sqlmodel import Session

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 AnalyticsImportBatch, AnalyticsPostSnapshot, ExternalPost, Provider, Theme, VideoAsset, VideoStatus
from app.services.analytics_mapping import mapping_candidates_for


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 csv_video(title="Real CSV Title", views=42):
    return ("Videos,Videotitel,Veröffentlichungszeitpunkt des Videos,Dauer,Aufrufe,Wiedergabezeit (Stunden),Abonnenten,Impressionen,Klickrate der Impressionen (%)\n"
            f"Gesamt,,,,{views},0.1,0,10,1.0\n"
            f"yt_real,{title},\"Jun 5, 2026\",39,{views},0.07,1,10,2.5\n").encode()


def csv_daily():
    return b"Datum,Aufrufe\n2026-06-05,42\n"


def csv_chart():
    return b"Datum,Videos,Videotitel,Ver\xc3\xb6ffentlichungszeitpunkt des Videos,Dauer,Aufrufe\n2026-06-05,yt_real,Real CSV Title,Jun 5, 2026,39,42\n"


def test_settings_use_correct_sprint7_paths(tmp_path):
    _, client = setup_test_db(tmp_path)
    settings = client.get("/api/settings").json()["settings"]
    assert settings["google_drive_analytics_folder"] == "TrueTraceShorts/Analysen/260606 Youtube"
    assert settings["youtube_analytics_import_folder"] == "storage/imports/youtube/260606"
    assert settings["website_branch"] == "website-mvp-v0"


def test_youtube_folder_imports_id_named_tabellendaten_with_optional_warnings(tmp_path):
    _, client = setup_test_db(tmp_path)
    folder = tmp_path / "imports"
    folder.mkdir()
    (folder / "opaque-google-file-id.csv").write_bytes(csv_video("This Small Fee Can Expose Your Card", 120))
    response = client.post(f"/api/analytics/import/youtube-folder?folder={folder}&source_label=Youtube%20260606")
    assert response.status_code == 200
    body = response.json()
    assert body["batch"]["source_label"] == "Youtube 260606"
    assert body["batch"]["is_demo"] is False
    assert body["batch"]["status"] == "imported_with_warnings"
    assert body["created_snapshots"] == 1
    assert "Tabellendaten" in body["recognized_files"]
    assert "Gesamtwerte" in body["missing_files"]
    assert any("Gesamtwerte.csv missing" in warning for warning in body["warnings"])
    results = client.get("/api/analytics/video-results").json()["items"]
    assert results[0]["title"] == "This Small Fee Can Expose Your Card"
    assert results[0]["views"] == 120
    assert results[0]["is_demo"] is False


def test_youtube_folder_blocks_without_tabellendaten(tmp_path):
    _, client = setup_test_db(tmp_path)
    folder = tmp_path / "imports"
    folder.mkdir()
    (folder / "260606_Gesamtwerte.csv").write_bytes(csv_daily())
    response = client.post(f"/api/analytics/import/youtube-folder?folder={folder}&source_label=Youtube%20260606")
    assert response.status_code == 400
    assert "Tabellendaten CSV is required" in response.json()["detail"]["message"]


def test_youtube_folder_status_reports_recognized_missing_and_warnings(tmp_path):
    _, client = setup_test_db(tmp_path)
    folder = tmp_path / "imports"
    folder.mkdir()
    (folder / "opaque.csv").write_bytes(csv_video("Header Detected", 9))
    response = client.get(f"/api/analytics/youtube-folder-status?folder={folder}")
    assert response.status_code == 200
    body = response.json()
    assert body["recognized"]["Tabellendaten"]["file_name"] == "opaque.csv"
    assert "Gesamtwerte" in body["missing"]
    assert "Diagrammdaten" in body["missing"]


def test_canonicalizer_copies_id_named_csv_by_header(tmp_path):
    source = tmp_path / "source"
    target = tmp_path / "target"
    source.mkdir()
    target.mkdir()
    (source / "google-file-id.csv").write_bytes(csv_video("Canonical Title", 55))
    script = Path(__file__).resolve().parents[2] / "scripts" / "canonicalize_youtube_csvs.py"
    result = subprocess.run(
        ["python", str(script), "--source-dir", str(source), "--target-dir", str(target), "--date-prefix", "260606"],
        text=True,
        capture_output=True,
        check=True,
    )
    report = json.loads(result.stdout)
    assert report["recognized"]["Tabellendaten"]["source"] == "google-file-id.csv"
    canonical = target / "260606_Tabellendaten.csv"
    assert canonical.exists()
    assert canonical.read_bytes() == (source / "google-file-id.csv").read_bytes()


def test_youtube_folder_imports_exact_three_real_files_and_not_demo(tmp_path):
    _, client = setup_test_db(tmp_path)
    folder = tmp_path / "imports"
    folder.mkdir()
    (folder / "260606_Diagrammdaten.csv").write_bytes(csv_chart())
    (folder / "260606_Gesamtwerte.csv").write_bytes(csv_daily())
    (folder / "260606_Tabellendaten.csv").write_bytes(csv_video("This Small Fee Can Expose Your Card", 120))
    response = client.post(f"/api/analytics/import/youtube-folder?folder={folder}&source_label=Youtube%20260606")
    assert response.status_code == 200
    body = response.json()
    assert body["batch"]["source_label"] == "Youtube 260606"
    assert body["created_snapshots"] == 1
    results = client.get("/api/analytics/video-results").json()["items"]
    assert results[0]["title"] == "This Small Fee Can Expose Your Card"
    assert results[0]["is_demo"] is False


def test_mapping_sources_match_website_candidate_markdown_and_upload_audit_by_youtube_id():
    website_candidate = mapping_candidates_for("Your Page Will Be Deleted? Don’t Click Yet", "jBVo4l5vdEY", limit=3)
    assert website_candidate[0]["confidence"] == "high"
    assert website_candidate[0]["website_slug"] == "fake-page-deletion-warning"
    assert "YouTube video ID" in website_candidate[0]["reason"]

    markdown = mapping_candidates_for("This Delivery Fee Text Is a Trap", "iDqRkqmL-QY", limit=3)
    assert markdown[0]["confidence"] == "high"
    assert markdown[0]["website_slug"] == "delivery-sms-trap"

    audit = mapping_candidates_for("Never Read This Bank Code Out Loud", "QXqtLy7r4kc", limit=3)
    assert audit[0]["confidence"] == "high"
    assert audit[0]["candidate_id"] == "erf-003-bank-code-scam"


def test_auto_link_preview_and_bulk_link_high_confidence_only(tmp_path):
    _, client = setup_test_db(tmp_path)
    folder = tmp_path / "imports"
    folder.mkdir()
    csv = (
        "Videos,Videotitel,Veröffentlichungszeitpunkt des Videos,Dauer,Aufrufe,Wiedergabezeit (Stunden),Abonnenten,Impressionen,Klickrate der Impressionen (%)\n"
        "Gesamt,,,,121,0.1,0,10,1.0\n"
        "jBVo4l5vdEY,Your Page Will Be Deleted? Don’t Click Yet #cybersecurity #onlinesafety,\"Jun 5, 2026\",39,80,0.07,1,10,2.5\n"
        "unknown123,Very Fuzzy Unknown Title,\"Jun 5, 2026\",39,41,0.07,0,10,2.5\n"
    ).encode()
    (folder / "opaque.csv").write_bytes(csv)
    assert client.post(f"/api/analytics/import/youtube-folder?folder={folder}&source_label=Youtube%20260606").status_code == 200
    preview = client.get("/api/analytics/auto-link-preview").json()
    assert preview["count"] == 1
    assert preview["items"][0]["external_post_id"] == "jBVo4l5vdEY"
    linked = client.post("/api/analytics/auto-link-high-confidence").json()
    assert linked["linked_count"] == 1
    simple = client.get("/api/analytics/simple").json()
    assert simple["mapping"]["linked"] == 1
    assert simple["mapping"]["unlinked"] == 1
    results = client.get("/api/analytics/video-results").json()["items"]
    linked_row = next(item for item in results if item["youtube_id"] == "jBVo4l5vdEY")
    assert linked_row["topic_title"]
    assert linked_row["script_title"]
    assert linked_row["website_slug"] == "fake-page-deletion-warning"
    unknown = next(item for item in results if item["youtube_id"] == "unknown123")
    assert unknown["linked"] is False


def test_website_status_shows_pending_update_without_push(tmp_path):
    _, client = setup_test_db(tmp_path)
    response = client.get("/api/website/status")
    assert response.status_code == 200
    body = response.json()
    assert "auto_push" in body
    assert body["auto_push"] is False


def test_mapping_family_and_ignore_actions(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        family = Theme(title="Everyday Red Flags")
        video = VideoAsset(candidate_id="v1", package_id="p1", source="prepared_package", file_path="/tmp/v.mp4", file_size=1, checksum="c1", working_title="Prepared Package", topic_id=family.id, status=VideoStatus.ready_for_review)
        batch = AnalyticsImportBatch(provider=Provider.youtube, source_label="Youtube 260606", is_demo=False, status="imported")
        session.add(family); session.add(video); session.add(batch); session.commit(); session.refresh(family); session.refresh(video); session.refresh(batch)
        post = ExternalPost(provider=Provider.youtube, external_post_id="yt_unlinked", title="Prepared Package", source_label="Youtube 260606", is_demo=False)
        snap = AnalyticsPostSnapshot(batch_id=batch.id, provider=Provider.youtube, external_post_id="yt_unlinked", title="Prepared Package", views=10, source_label="Youtube 260606", is_demo=False, snapshot_at=datetime.now(timezone.utc))
        session.add(post); session.add(snap); session.commit()
        family_id = family.id
    r = client.post("/api/analytics/map-post-family", json={"external_post_id": "yt_unlinked", "topic_id": family_id})
    assert r.status_code == 200
    assert r.json()["post"]["mapping_status"] == "family_only"
    r = client.post("/api/analytics/ignore-post", json={"external_post_id": "yt_unlinked", "reason": "not relevant"})
    assert r.status_code == 200
    assert client.get("/api/analytics/unlinked-posts").json()["items"] == []


def test_website_status_and_prepare_payload(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video = VideoAsset(candidate_id="erf-027-small-fee", package_id="p1", source="prepared_package", file_path="/tmp/v.mp4", file_size=1, checksum="web1", working_title="Small delivery fee scam", status=VideoStatus.ready_for_review)
        session.add(video); session.commit(); session.refresh(video); video_id = video.id
    status = client.get("/api/website/status")
    assert status.status_code == 200
    response = client.post(f"/api/website/companions/{video_id}/prepare-payload")
    assert response.status_code == 200
    payload = response.json()["payload"]
    assert payload["slug"] == "small-delivery-fee-scam"
    assert payload["videoUrl"] is None
    assert payload["checklist"]
