from datetime import datetime
from typing import Any

from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select

from app.api.deps import get_session
from app.models.core import ActivityEvent, CreativeChange, MetadataVersion, PostDraft, VideoAsset, now_utc
from app.services.post_draft_mutations import apply_draft_patch

router = APIRouter(prefix="/post-drafts", tags=["post-drafts"])

FIELD_TO_CHANGE_TYPE = {
    "title": "title",
    "description": "description",
    "caption": "caption",
    "hashtags_json": "hashtags",
    "tags_json": "hashtags",
    "scheduled_at": "schedule",
}


def serialize(d: PostDraft):
    return d.model_dump(mode="json")


def parse_optional_datetime(value):
    if value in (None, ""):
        return None
    if isinstance(value, datetime):
        return value
    if isinstance(value, str):
        normalized = value.replace("Z", "+00:00")
        try:
            return datetime.fromisoformat(normalized)
        except ValueError as exc:
            raise HTTPException(400, "scheduled_at must be ISO datetime") from exc
    raise HTTPException(400, "scheduled_at must be ISO datetime")


def _text(value: Any) -> str | None:
    if value in (None, ""):
        return None
    if isinstance(value, list):
        return " ".join(str(item) for item in value)
    if isinstance(value, datetime):
        return value.isoformat()
    return str(value)


def add_activity(session: Session, entity_type: str, entity_id: str, event_type: str, label: str, payload: dict | None = None, created_by: str = "creator"):
    session.add(ActivityEvent(
        entity_type=entity_type,
        entity_id=entity_id,
        event_type=event_type,
        label=label,
        payload_json=payload or {},
        created_by=created_by,
    ))


def add_creative_changes(session: Session, draft: PostDraft, diff: dict, payload: dict):
    change_note = payload.get("change_note") or payload.get("changeNote")
    hypothesis = payload.get("hypothesis")
    created_by = payload.get("edited_by") or payload.get("created_by") or "creator"
    for field, change in diff.items():
        change_type = FIELD_TO_CHANGE_TYPE.get(field)
        if not change_type:
            continue
        session.add(CreativeChange(
            video_asset_id=draft.video_asset_id,
            post_draft_id=draft.id,
            topic_id=None,
            change_type=change_type,
            before_text=_text(change.get("before")),
            after_text=_text(change.get("after")),
            change_note=change_note or f"{change_type.title()} edited on review page",
            hypothesis=hypothesis,
            created_by=created_by,
            source="metadata_version",
        ))
    if change_note and not any(FIELD_TO_CHANGE_TYPE.get(field) for field in diff):
        session.add(CreativeChange(
            video_asset_id=draft.video_asset_id,
            post_draft_id=draft.id,
            change_type=payload.get("change_type") or "other",
            change_note=change_note,
            hypothesis=hypothesis,
            created_by=created_by,
            source="manual",
        ))


@router.get("")
def list_drafts(provider: str | None = None, session: Session = Depends(get_session)):
    q = select(PostDraft)
    if provider:
        q = q.where(PostDraft.provider == provider)
    return {"items": [serialize(x) for x in session.exec(q.order_by(PostDraft.created_at.desc())).all()]}


@router.post("")
def create_draft(payload: dict, session: Session = Depends(get_session)):
    draft = PostDraft(**payload)
    session.add(draft)
    add_activity(session, "post_draft", draft.id, "created", "Draft created", {"provider": str(draft.provider)})
    session.commit()
    session.refresh(draft)
    return serialize(draft)


@router.get("/{draft_id}")
def get_draft(draft_id: str, session: Session = Depends(get_session)):
    draft = session.get(PostDraft, draft_id)
    if not draft:
        raise HTTPException(404, "Draft not found")
    versions = session.exec(select(MetadataVersion).where(MetadataVersion.post_draft_id == draft_id).order_by(MetadataVersion.edited_at.desc())).all()
    changes = session.exec(select(CreativeChange).where(CreativeChange.post_draft_id == draft_id).order_by(CreativeChange.changed_at.desc())).all()
    activity = session.exec(select(ActivityEvent).where(ActivityEvent.entity_type == "post_draft", ActivityEvent.entity_id == draft_id).order_by(ActivityEvent.created_at.desc())).all()
    data = serialize(draft)
    data["versions"] = [v.model_dump(mode="json") for v in versions]
    data["changes"] = [c.model_dump(mode="json") for c in changes]
    data["activity"] = [a.model_dump(mode="json") for a in activity]
    return data


@router.patch("/{draft_id}")
def patch_draft(draft_id: str, payload: dict, session: Session = Depends(get_session)):
    draft = session.get(PostDraft, draft_id)
    if not draft:
        raise HTTPException(404, "Draft not found")
    apply_draft_patch(session, draft, payload, invalidate_uploads=True)
    session.commit()
    session.refresh(draft)
    return serialize(draft)


@router.post("/{draft_id}/approve")
def approve(draft_id: str, session: Session = Depends(get_session)):
    draft = session.get(PostDraft, draft_id)
    if not draft:
        raise HTTPException(404, "Draft not found")
    draft.status = "approved"
    draft.approved_at = now_utc()
    draft.updated_at = now_utc()
    session.add(draft)
    add_activity(session, "post_draft", draft.id, "approved", "Approved for private upload", {"provider": str(draft.provider)})
    add_activity(session, "video", draft.video_asset_id, "approved", "Approved for private upload", {"post_draft_id": draft.id})
    session.commit()
    return {"ok": True, "status": draft.status}


@router.post("/{draft_id}/schedule")
def schedule(draft_id: str, payload: dict, session: Session = Depends(get_session)):
    draft = session.get(PostDraft, draft_id)
    if not draft:
        raise HTTPException(404, "Draft not found")
    before = draft.scheduled_at
    draft.scheduled_at = parse_optional_datetime(payload.get("scheduled_at"))
    draft.status = "scheduled"
    draft.updated_at = now_utc()
    session.add(draft)
    session.add(CreativeChange(
        video_asset_id=draft.video_asset_id,
        post_draft_id=draft.id,
        change_type="schedule",
        before_text=_text(before),
        after_text=_text(draft.scheduled_at),
        change_note=payload.get("change_note") or "Schedule selected",
        source="metadata_version",
    ))
    add_activity(session, "post_draft", draft.id, "scheduled", "Scheduled", {"scheduled_at": _text(draft.scheduled_at)})
    add_activity(session, "video", draft.video_asset_id, "scheduled", "Scheduled", {"post_draft_id": draft.id, "scheduled_at": _text(draft.scheduled_at)})
    session.commit()
    return {"ok": True, "status": draft.status}
