from datetime import datetime, timedelta, timezone

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


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 create_video_with_draft(session: Session) -> tuple[VideoAsset, PostDraft]:
    video = VideoAsset(
        candidate_id="sprint5_video",
        package_id="package_sprint5",
        source="prepared_package",
        file_path="/tmp/video.mp4",
        file_size=1,
        checksum="sprint5-checksum",
        working_title="Login Prompt You Didn’t Start? Don’t Approve",
        status=VideoStatus.ready_for_review,
    )
    session.add(video)
    session.commit()
    session.refresh(video)
    draft = PostDraft(video_asset_id=video.id, provider=Provider.youtube, title="Old title", description="Old description", privacy_status="private")
    session.add(draft)
    session.commit()
    session.refresh(draft)
    return video, draft


def add_snapshots(session: Session, video: VideoAsset | None = None, external_id: str = "yt123"):
    batch1 = AnalyticsImportBatch(provider=Provider.youtube, snapshot_at=datetime(2026, 6, 3, tzinfo=timezone.utc), status="imported")
    batch2 = AnalyticsImportBatch(provider=Provider.youtube, snapshot_at=datetime(2026, 6, 6, tzinfo=timezone.utc), status="imported")
    session.add(batch1)
    session.add(batch2)
    session.commit()
    post = ExternalPost(provider=Provider.youtube, external_post_id=external_id, title="Login Prompt You Didn’t Start? Don’t Approve", video_asset_id=video.id if video else None)
    session.add(post)
    session.commit()
    session.refresh(post)
    for batch, views in [(batch1, 80), (batch2, 120)]:
        session.add(AnalyticsPostSnapshot(
            batch_id=batch.id,
            provider=Provider.youtube,
            external_post_id=external_id,
            video_asset_id=video.id if video else None,
            snapshot_at=batch.snapshot_at,
            title="Login Prompt You Didn’t Start? Don’t Approve",
            views=views,
            views_delta=40 if views == 120 else None,
            impression_ctr_pct=3.2,
            retention_proxy_pct=11.6,
            subscribers_delta=2,
            published_at=datetime(2026, 6, 1, tzinfo=timezone.utc),
            trend="rising" if views == 120 else "new",
        ))
    session.commit()
    return post


def test_metadata_save_creates_creative_change_and_activity(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video, draft = create_video_with_draft(session)
        draft_id = draft.id
        video_id = video.id

    response = client.patch(f"/api/post-drafts/{draft_id}", json={"title": "New title", "change_note": "Made warning clearer"})

    assert response.status_code == 200
    detail = client.get(f"/api/post-drafts/{draft_id}").json()
    assert any(change["change_type"] == "title" and change["before_text"] == "Old title" and change["after_text"] == "New title" for change in detail["changes"])
    assert any(event["event_type"] == "metadata_saved" for event in detail["activity"])
    video_detail = client.get(f"/api/videos/{video_id}").json()
    assert any(event["event_type"] == "metadata_saved" for event in video_detail["activity"])


def test_video_results_endpoint_returns_views_and_link_status(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video, _ = create_video_with_draft(session)
        add_snapshots(session, video=video)

    response = client.get("/api/analytics/video-results")

    assert response.status_code == 200
    item = response.json()["items"][0]
    assert item["views"] == 120
    assert item["linked"] is True
    assert item["views_delta_label"] == "+40 since last import"


def test_improvements_endpoint_compares_before_after_snapshot(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video, draft = create_video_with_draft(session)
        add_snapshots(session, video=video)
        draft_id = draft.id

    client.patch(f"/api/post-drafts/{draft_id}", json={"title": "New title", "change_note": "Made warning clearer"})
    response = client.get("/api/analytics/improvements")

    assert response.status_code == 200
    items = response.json()["items"]
    assert items
    assert items[0]["change_type"] == "title"
    assert items[0]["status"] in {"measured", "needs_more_snapshots"}


def test_unlinked_posts_endpoint_lists_unmapped_posts(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        add_snapshots(session, video=None, external_id="unlinked1")

    response = client.get("/api/analytics/unlinked-posts")

    assert response.status_code == 200
    assert response.json()["items"][0]["external_post_id"] == "unlinked1"


def test_manual_change_endpoint_creates_activity(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video, draft = create_video_with_draft(session)
        video_id = video.id
        draft_id = draft.id

    response = client.post("/api/analytics/changes", json={"video_asset_id": video_id, "post_draft_id": draft_id, "change_type": "hook", "change_note": "Shortened first sentence"})

    assert response.status_code == 200
    detail = client.get(f"/api/videos/{video_id}").json()
    assert any(change["change_type"] == "hook" for change in detail["changes"])
    assert any(event["event_type"] == "creative_change" for event in detail["activity"])
