from __future__ import annotations

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

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

from app.api.deps import get_session
from app.api.routes.settings import DEFAULT_SETTINGS, _settings as dashboard_settings
from app.models.core import ActivityEvent, ContentScript, PostDraft, VideoAsset, WebsiteCompanion

router = APIRouter(prefix="/website", tags=["website"])

UNSAFE_PATTERNS = [
    re.compile(r"https?://", re.I),
    re.compile(r"\b\+?\d[\d\s().-]{7,}\d\b"),
    re.compile(r"\b(?:\d[ -]*?){13,19}\b"),
    re.compile(r"\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b"),
]


def _settings(session: Session) -> dict:
    return dashboard_settings(session)


def _repo_path(session: Session) -> Path:
    value = _settings(session).get("website_repo_path") or DEFAULT_SETTINGS.get("website_repo_path") or "/home/agent/projects/TrueTraceShorts_WebSite"
    return Path(value).expanduser()


def _run(cmd: list[str], cwd: Path, timeout: int = 180) -> dict:
    proc = subprocess.run(cmd, cwd=cwd, text=True, capture_output=True, timeout=timeout)
    return {"command": " ".join(cmd), "returncode": proc.returncode, "stdout": proc.stdout[-4000:], "stderr": proc.stderr[-4000:]}


def _slug(value: str) -> str:
    text = ''.join(ch.lower() if ch.isalnum() else '-' for ch in value)
    text = re.sub(r"-+", "-", text).strip("-")
    return text[:80] or "untitled-guide"


def _safe_text(payload: dict) -> list[str]:
    warnings = []
    for key in ["visibleScreen", "redFlag", "whyItWorks", "saferMove", "body", "metaDescription"]:
        value = str(payload.get(key) or "")
        for pat in UNSAFE_PATTERNS:
            if pat.search(value):
                warnings.append(f"Unsafe text in {key}")
    return warnings


def _companion_for(session: Session, video_asset_id: str) -> WebsiteCompanion:
    comp = session.exec(select(WebsiteCompanion).where(WebsiteCompanion.video_asset_id == video_asset_id)).first()
    if comp:
        return comp
    video = session.get(VideoAsset, video_asset_id)
    if not video:
        raise HTTPException(status_code=404, detail="Video not found")
    comp = WebsiteCompanion(video_asset_id=video_asset_id, website_repo_path=str(_repo_path(session)), website_branch=_settings(session).get("website_branch") or "website-mvp-v0")
    session.add(comp)
    session.commit()
    session.refresh(comp)
    return comp


def _build_payload(session: Session, video: VideoAsset, companion: WebsiteCompanion) -> dict:
    yt = session.exec(select(PostDraft).where(PostDraft.video_asset_id == video.id, PostDraft.provider == "youtube")).first()
    script = session.exec(select(ContentScript).where(ContentScript.video_asset_id == video.id)).first()
    title = video.working_title or (yt.title if yt else None) or video.original_file_name or video.candidate_id
    slug = companion.slug or _slug(title)
    family = video.series or video.content_pillar or "Everyday Red Flags"
    hook = (script.hook if script else None) or "A small online request can hide a bigger risk."
    body = (script.script_text if script and script.script_text else "Use this calm guide to check the warning sign, avoid the message link, and verify the request through the official app or website.")
    youtube_video_id = companion.youtube_video_id or video.external_youtube_id or (yt.external_post_id if yt else None)
    video_url_internal = f"https://www.youtube.com/shorts/{youtube_video_id}" if youtube_video_id else companion.video_url_internal
    public_video_url = video_url_internal if (video_url_internal and companion.website_link_allowed) else None
    payload = {
        "id": video.candidate_id,
        "slug": slug,
        "title": title,
        "shortTitle": title[:48],
        "hook": hook,
        "category": family,
        "riskLevel": "medium",
        "visibleScreen": "A phone screen showing a suspicious small online request.",
        "redFlag": "The request pushes you to act before you verify it independently.",
        "whyItWorks": "Small requests feel harmless and urgent, so people skip verification.",
        "saferMove": "Open the official app or website directly instead of using the message link.",
        "ifAlreadyClicked": ["Do not enter more information.", "Monitor the account or card if payment details were entered.", "Use the official app or website to verify the status."],
        "checklist": ["Did the request arrive by message?", "Does it ask for payment or login details?", "Can you verify it in the official app?"],
        "checklistDetails": [{"title": "Verify inside the official app", "why": "A real issue should appear there.", "safer": "Open the app yourself, not the message link."}],
        "videoUrl": public_video_url,
        "source": {
            "dashboard_video_asset_id": video.id,
            "dashboard_post_draft_id": yt.id if yt else companion.post_draft_id,
            "youtube_video_id": youtube_video_id,
            "video_url_internal": video_url_internal,
            "website_link_allowed": companion.website_link_allowed,
            "public_video_url_visible": bool(public_video_url),
        },
        "thumbnail": None,
        "screenImage": None,
        "screenImageAlt": "Phone screen with suspicious online request.",
        "visualBrief": "Direct phone view of a suspicious online request, no real brands or phone numbers.",
        "related": ["delivery-sms-trap"],
        "toolRelevance": "Account controls and official apps help after the safer move.",
        "affiliateCategory": "payment-safety",
        "seoTitle": f"{title}: what to check before you act",
        "metaDescription": "Learn what to check before reacting to a suspicious online request.",
        "socialTitle": title[:64],
        "body": body,
        "faq": [{"question": "Should I act from the message link?", "answer": "Open the official app or website directly first. Do not use the message link."}],
        "lastUpdated": datetime.now(timezone.utc).date().isoformat(),
    }
    return payload


@router.get("/status")
def website_status(session: Session = Depends(get_session)):
    settings = _settings(session)
    repo = _repo_path(session)
    exists = repo.exists()
    branch = None
    dirty = None
    qa = None
    pending_files = []
    pending_guide = None
    if exists:
        branch_run = _run(["git", "branch", "--show-current"], repo, timeout=20)
        branch = branch_run["stdout"].strip() if branch_run["returncode"] == 0 else None
        status_run = _run(["git", "status", "--short"], repo, timeout=20)
        if status_run["returncode"] == 0:
            pending_files = [line.strip() for line in status_run["stdout"].splitlines() if line.strip()]
            dirty = bool(pending_files)
    companions = session.exec(select(WebsiteCompanion).order_by(WebsiteCompanion.updated_at.desc())).all()
    ready = next((c for c in companions if c.status in {"commit_ready", "qa_passed", "generated"}), companions[0] if companions else None)
    if ready:
        pending_guide = {"title": ready.title, "slug": ready.slug, "status": ready.status, "files": pending_files, "file_count": len(pending_files), "push_status": "waiting for approval" if pending_files else "no pending files"}
    return {"repo_path": str(repo), "repo_exists": exists, "current_branch": branch, "expected_branch": settings.get("website_branch"), "uncommitted_changes": dirty, "pending_files": pending_files, "pending_guide": pending_guide, "public_url": settings.get("website_public_url"), "auto_push": settings.get("website_auto_push", False), "last_qa_status": qa}


@router.get("/companions")
def list_companions(session: Session = Depends(get_session)):
    rows = session.exec(select(WebsiteCompanion).order_by(WebsiteCompanion.updated_at.desc())).all()
    return {"items": [r.model_dump(mode="json") for r in rows]}


@router.get("/companions/{video_asset_id}")
def get_companion(video_asset_id: str, session: Session = Depends(get_session)):
    return {"item": _companion_for(session, video_asset_id).model_dump(mode="json")}


@router.post("/companions/{video_asset_id}/prepare-payload")
def prepare_payload(video_asset_id: str, session: Session = Depends(get_session)):
    video = session.get(VideoAsset, video_asset_id)
    if not video:
        raise HTTPException(status_code=404, detail="Video not found")
    comp = _companion_for(session, video_asset_id)
    payload = _build_payload(session, video, comp)
    warnings = _safe_text(payload)
    if warnings:
        comp.status = "failed"
        comp.last_error = "; ".join(warnings)
        session.add(comp); session.commit()
        raise HTTPException(status_code=400, detail={"message": "Website safety validation failed", "warnings": warnings})
    comp.candidate_payload_json = payload
    comp.slug = payload["slug"]
    comp.title = payload["title"]
    source = payload.get("source") or {}
    if source.get("youtube_video_id"):
        comp.youtube_video_id = source.get("youtube_video_id")
        comp.video_url_internal = source.get("video_url_internal")
        comp.website_link_allowed = bool(source.get("website_link_allowed"))
        comp.status = "payload_ready_with_public_video" if comp.website_link_allowed else "ready_without_public_video"
    else:
        comp.status = "payload_ready"
    comp.updated_at = datetime.now(timezone.utc)
    session.add(comp)
    session.add(ActivityEvent(entity_type="website_companion", entity_id=comp.id, event_type="payload_prepared", label="Website payload prepared", payload_json={"video_asset_id": video_asset_id, "slug": comp.slug}))
    session.commit(); session.refresh(comp)
    return {"ok": True, "item": comp.model_dump(mode="json"), "payload": payload}


@router.post("/companions/{video_asset_id}/generate")
def generate(video_asset_id: str, session: Session = Depends(get_session)):
    comp = _companion_for(session, video_asset_id)
    if not comp.candidate_payload_json:
        prepare_payload(video_asset_id, session)
        comp = _companion_for(session, video_asset_id)
    repo = _repo_path(session)
    if not repo.exists():
        raise HTTPException(status_code=404, detail=f"Website repo missing: {repo}")
    candidate_dir = repo / (_settings(session).get("website_candidate_dir") or "data/candidates")
    candidate_dir.mkdir(parents=True, exist_ok=True)
    candidate_id = comp.candidate_payload_json.get("id") or comp.video_asset_id
    candidate_path = candidate_dir / f"{candidate_id}.json"
    candidate_path.write_text(json.dumps(comp.candidate_payload_json, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    video = session.get(VideoAsset, video_asset_id)
    cmd = ["npm", "run", "generate:companion", "--", "--candidate", str(candidate_path.relative_to(repo))]
    if video and video.file_path:
        video_path = Path(video.file_path).expanduser()
        if not video_path.is_absolute():
            video_path = Path(__file__).resolve().parents[3] / video_path
        cmd += ["--video", str(video_path.resolve())]
    run = _run(cmd, repo, timeout=240)
    comp.generated_content_path = f"src/content/redflags/{comp.slug}.md"
    comp.status = "generated" if run["returncode"] == 0 else "failed"
    comp.last_error = None if run["returncode"] == 0 else (run["stderr"] or run["stdout"])
    comp.updated_at = datetime.now(timezone.utc)
    session.add(comp)
    session.add(ActivityEvent(entity_type="website_companion", entity_id=comp.id, event_type="generated" if run["returncode"] == 0 else "generate_failed", label="Website companion generated" if run["returncode"] == 0 else "Website generation failed", payload_json=run))
    session.commit(); session.refresh(comp)
    return {"ok": run["returncode"] == 0, "item": comp.model_dump(mode="json"), "run": run}


@router.post("/companions/{video_asset_id}/run-qa")
def run_qa(video_asset_id: str, session: Session = Depends(get_session)):
    comp = _companion_for(session, video_asset_id)
    repo = _repo_path(session)
    if not repo.exists():
        raise HTTPException(status_code=404, detail=f"Website repo missing: {repo}")
    run = _run(["npm", "run", "qa:production"], repo, timeout=300)
    comp.qa_status_json = run
    comp.status = "qa_passed" if run["returncode"] == 0 else "failed"
    comp.last_error = None if run["returncode"] == 0 else (run["stderr"] or run["stdout"])
    comp.updated_at = datetime.now(timezone.utc)
    session.add(comp)
    session.add(ActivityEvent(entity_type="website_companion", entity_id=comp.id, event_type="qa_passed" if run["returncode"] == 0 else "qa_failed", label="Website QA passed" if run["returncode"] == 0 else "Website QA failed", payload_json=run))
    session.commit(); session.refresh(comp)
    return {"ok": run["returncode"] == 0, "item": comp.model_dump(mode="json"), "run": run}


@router.post("/companions/{video_asset_id}/prepare-commit")
def prepare_commit(video_asset_id: str, session: Session = Depends(get_session)):
    comp = _companion_for(session, video_asset_id)
    repo = _repo_path(session)
    if comp.status not in {"qa_passed", "commit_ready"}:
        raise HTTPException(status_code=400, detail="Website QA must pass before preparing commit")
    status = _run(["git", "status", "--short"], repo, timeout=20)
    diff = _run(["git", "diff", "--", "src/content/redflags", "data/candidates", "public/redflags/thumbnails"], repo, timeout=30)
    comp.git_status_json = {"status": status, "diff": diff}
    comp.status = "commit_ready"
    comp.updated_at = datetime.now(timezone.utc)
    session.add(comp)
    session.add(ActivityEvent(entity_type="website_companion", entity_id=comp.id, event_type="commit_prepared", label="Website commit prepared", payload_json={"status": status}))
    session.commit(); session.refresh(comp)
    return {"ok": True, "item": comp.model_dump(mode="json"), "git_status": status, "git_diff": diff}


@router.post("/companions/{video_asset_id}/mark-pushed")
def mark_pushed(video_asset_id: str, session: Session = Depends(get_session)):
    comp = _companion_for(session, video_asset_id)
    comp.status = "pushed"
    comp.updated_at = datetime.now(timezone.utc)
    session.add(comp)
    session.add(ActivityEvent(entity_type="website_companion", entity_id=comp.id, event_type="pushed", label="Website update marked pushed", payload_json={"video_asset_id": video_asset_id}))
    session.commit(); session.refresh(comp)
    return {"ok": True, "item": comp.model_dump(mode="json")}
