"""Reports + Actions: internal review, client review, PDF export, comments."""

import json
from datetime import datetime, timezone

from fastapi import APIRouter, Form, HTTPException, Request, Response
from fastapi.responses import HTMLResponse, RedirectResponse

import auth
from db import execute, query, query_one
from deps import (APP_BASE_URL, BASE_DIR, DEV_MAGIC_LINK_PRINT, audit,
                  jinja_env, render, require_internal, require_user,
                  user_can_access_client)

router = APIRouter()


# ---------- Internal: report detail + edit ----------

@router.get("/reports/{report_id}", response_class=HTMLResponse)
def report_detail(request: Request, report_id: int):
    user = require_internal(request)
    report = query_one(
        """SELECT r.*, c.id AS client_id, c.name AS client_name, c.primary_url,
                  o.id AS org_id, o.name AS org_name
           FROM report r
           JOIN client c ON c.id = r.client_id
           JOIN organization o ON o.id = c.organization_id
           WHERE r.id = ?""", (report_id,)
    )
    if not report:
        raise HTTPException(404)
    actions_with_spec = _load_actions(report_id, include_pretty=True)
    reviewers = query(
        """SELECT u.id, u.email FROM user_client_access uca
           JOIN user u ON u.id = uca.user_id
           WHERE uca.client_id = ? AND u.role = 'client'""",
        (report["client_id"],),
    )
    return render("report_detail.html", user=user, report=report,
                  actions=actions_with_spec, reviewers=reviewers)


@router.post("/reports/{report_id}/title")
def report_update_title(
    request: Request, report_id: int,
    title: str = Form(...), summary_md: str = Form(""),
):
    user = require_internal(request)
    r = query_one("SELECT id, client_id FROM report WHERE id = ?", (report_id,))
    if not r:
        raise HTTPException(404)
    execute("UPDATE report SET title = ?, summary_md = ? WHERE id = ?",
            (title.strip(), summary_md, report_id))
    audit(user.id, r["client_id"], "report.update_meta", {"report_id": report_id})
    return RedirectResponse(f"/reports/{report_id}", status_code=303)


@router.post("/reports/{report_id}/send")
def report_send_to_client(request: Request, report_id: int):
    user = require_internal(request)
    r = query_one("SELECT id, client_id, status FROM report WHERE id = ?", (report_id,))
    if not r:
        raise HTTPException(404)
    reviewers = query(
        """SELECT u.id, u.email FROM user_client_access uca
           JOIN user u ON u.id = uca.user_id
           WHERE uca.client_id = ? AND u.role = 'client'""",
        (r["client_id"],),
    )
    if not reviewers:
        raise HTTPException(400, "Invite at least one reviewer first.")
    execute(
        """UPDATE report SET status = 'sent', sent_to_client_at = CURRENT_TIMESTAMP
           WHERE id = ?""", (report_id,),
    )
    for rev in reviewers:
        token = auth.issue_magic_link(rev["id"], client_id_scope=r["client_id"])
        link = f"{APP_BASE_URL}/auth/magic?token={token}"
        if DEV_MAGIC_LINK_PRINT:
            print(f"================ MAGIC LINK for {rev['email']} (report {report_id}) ================\n"
                  f"{link}\n"
                  f"================================================================")
    audit(user.id, r["client_id"], "report.send",
          {"report_id": report_id, "n_reviewers": len(reviewers)})
    return RedirectResponse(f"/reports/{report_id}", status_code=303)


# ---------- Action edit/delete (internal) ----------

@router.post("/actions/{action_id}/edit")
def action_edit(
    request: Request, action_id: int,
    title: str = Form(...), notes_md: str = Form(""),
    proposed_spec_json: str = Form(...),
):
    user = require_internal(request)
    a = _action_with_client(action_id)
    if not a:
        raise HTTPException(404)
    try:
        json.loads(proposed_spec_json)
    except json.JSONDecodeError:
        raise HTTPException(400, "Spec must be valid JSON")
    execute(
        """UPDATE action SET title = ?, notes_md = ?, proposed_spec_json = ?,
                              status = 'edited' WHERE id = ?""",
        (title.strip(), notes_md, proposed_spec_json, action_id),
    )
    audit(user.id, a["client_id"], "action.edit", {"action_id": action_id})
    return RedirectResponse(f"/reports/{a['report_id']}#a{action_id}", status_code=303)


@router.post("/actions/{action_id}/delete")
def action_delete(request: Request, action_id: int):
    user = require_internal(request)
    a = _action_with_client(action_id)
    if not a:
        raise HTTPException(404)
    execute("DELETE FROM action WHERE id = ?", (action_id,))
    audit(user.id, a["client_id"], "action.delete", {"action_id": action_id})
    return RedirectResponse(f"/reports/{a['report_id']}", status_code=303)


# ---------- Client review ----------

@router.get("/reports/{report_id}/review", response_class=HTMLResponse)
def report_review(request: Request, report_id: int):
    user = require_user(request)
    report = query_one(
        """SELECT r.*, c.id AS client_id, c.name AS client_name, c.primary_url
           FROM report r JOIN client c ON c.id = r.client_id
           WHERE r.id = ?""", (report_id,)
    )
    if not report:
        raise HTTPException(404)
    if not user_can_access_client(user, report["client_id"]):
        raise HTTPException(403)
    if report["status"] == "draft" and not user.is_internal:
        raise HTTPException(403, "This report has not been shared yet.")
    if not user.is_internal and report["status"] in ("sent", "in_review"):
        execute("UPDATE report SET status = 'in_review' WHERE id = ?", (report_id,))
    actions_view = _load_actions(report_id, with_comments=True)
    n_pending = sum(1 for a in actions_view if a["status"] == "proposed")
    n_approved = sum(1 for a in actions_view if a["status"] == "approved")
    n_rejected = sum(1 for a in actions_view if a["status"] == "rejected")
    return render("report_review.html", user=user, report=report,
                  actions=actions_view, n_pending=n_pending,
                  n_approved=n_approved, n_rejected=n_rejected)


@router.post("/actions/{action_id}/review")
def action_review(
    request: Request, action_id: int,
    decision: str = Form(...), comment: str = Form(""),
):
    user = require_user(request)
    a = _action_with_client(action_id)
    if not a:
        raise HTTPException(404)
    if not user_can_access_client(user, a["client_id"]):
        raise HTTPException(403)
    if decision not in ("approved", "rejected", "proposed"):
        raise HTTPException(400, "Invalid decision")
    execute(
        """UPDATE action SET status = ?, reviewed_by_user_id = ?,
                              reviewed_at = CURRENT_TIMESTAMP WHERE id = ?""",
        (decision, user.id, action_id),
    )
    if comment.strip():
        execute(
            "INSERT INTO action_comment (action_id, user_id, body) VALUES (?, ?, ?)",
            (action_id, user.id, comment.strip()),
        )
    audit(user.id, a["client_id"], "action.review",
          {"action_id": action_id, "decision": decision})
    _maybe_complete_report(a["report_id"])
    return RedirectResponse(f"/reports/{a['report_id']}/review#a{action_id}",
                            status_code=303)


@router.post("/actions/{action_id}/comment")
def action_comment(request: Request, action_id: int, body: str = Form(...)):
    user = require_user(request)
    a = _action_with_client(action_id)
    if not a:
        raise HTTPException(404)
    if not user_can_access_client(user, a["client_id"]):
        raise HTTPException(403)
    body = body.strip()
    if body:
        execute(
            "INSERT INTO action_comment (action_id, user_id, body) VALUES (?, ?, ?)",
            (action_id, user.id, body),
        )
        audit(user.id, a["client_id"], "action.comment", {"action_id": action_id})
    return RedirectResponse(f"/reports/{a['report_id']}/review#a{action_id}",
                            status_code=303)


# ---------- PDF export ----------

@router.get("/reports/{report_id}/pdf")
def report_pdf(request: Request, report_id: int):
    user = require_user(request)
    report = query_one(
        """SELECT r.*, c.id AS client_id, c.name AS client_name, c.primary_url,
                  o.name AS org_name
           FROM report r JOIN client c ON c.id = r.client_id
           JOIN organization o ON o.id = c.organization_id
           WHERE r.id = ?""", (report_id,)
    )
    if not report:
        raise HTTPException(404)
    if not user_can_access_client(user, report["client_id"]):
        raise HTTPException(403)
    actions_view = _load_actions(report_id)

    html = jinja_env.get_template("report_pdf.html").render(
        report=report, actions=actions_view,
        generated_at=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC"),
    )

    from weasyprint import HTML
    pdf_bytes = HTML(string=html, base_url=str(BASE_DIR)).write_pdf()
    filename = (
        f"tracking-plan-{report['client_name'].replace(' ', '-').lower()}"
        f"-v{report['version']}.pdf"
    )
    return Response(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={"Content-Disposition": f'inline; filename="{filename}"'},
    )


# ---------- Helpers ----------

def _load_actions(report_id: int, *, include_pretty: bool = False,
                  with_comments: bool = False) -> list[dict]:
    actions = query(
        """SELECT a.*, u.email AS reviewer_email
           FROM action a LEFT JOIN user u ON u.id = a.reviewed_by_user_id
           WHERE a.report_id = ? ORDER BY a.sort_order, a.id""",
        (report_id,),
    )
    out: list[dict] = []
    for a in actions:
        d = dict(a)
        try:
            d["spec"] = json.loads(d["proposed_spec_json"]) if d["proposed_spec_json"] else {}
        except json.JSONDecodeError:
            d["spec"] = {}
        if include_pretty:
            d["spec_pretty"] = json.dumps(d["spec"], indent=2)
        if with_comments:
            d["comments"] = query(
                """SELECT c.*, u.email, u.name FROM action_comment c
                   JOIN user u ON u.id = c.user_id
                   WHERE c.action_id = ? ORDER BY c.created_at""",
                (a["id"],),
            )
        out.append(d)
    return out


def _action_with_client(action_id: int):
    return query_one(
        """SELECT a.id, a.report_id, r.client_id
           FROM action a JOIN report r ON r.id = a.report_id
           WHERE a.id = ?""", (action_id,)
    )


def _maybe_complete_report(report_id: int) -> None:
    """If every action has been approved or rejected, mark the report approved."""
    pending = query_one(
        """SELECT COUNT(*) AS n FROM action
           WHERE report_id = ? AND status IN ('proposed','edited')""",
        (report_id,),
    )
    if pending and pending["n"] == 0:
        execute(
            """UPDATE report SET status = 'approved', approved_at = CURRENT_TIMESTAMP
               WHERE id = ? AND status IN ('sent','in_review')""",
            (report_id,),
        )
