"""HTML dashboard routes."""

from __future__ import annotations

import json
from pathlib import Path
from urllib.parse import parse_qs, urlencode

from fastapi import APIRouter, Depends, Query, Request, status
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse, Response
from fastapi.templating import Jinja2Templates
from pydantic import ValidationError
from sqlalchemy.orm import Session

from app.core.config import settings
from app.core.db import get_db
from app.models.event import EventPhase, SeverityLevel
from app.schemas.engagement import EngagementCreate
from app.schemas.event import EventCreate
from app.services import (
    dashboard_service,
    engagements as engagement_service,
    events as event_service,
    notes_generator,
    report_generator,
)

TEMPLATES_DIR = Path(__file__).resolve().parents[2] / "templates"
PROJECT_ROOT = Path(__file__).resolve().parents[3]
try:
    templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
except Exception:  # pragma: no cover - dependency availability issue at runtime
    templates = None
else:
    templates.env.globals["app_name"] = settings.app_name

router = APIRouter(tags=["dashboard"])


def _render_template(
    request: Request,
    name: str,
    context: dict[str, object],
    status_code: int = status.HTTP_200_OK,
) -> HTMLResponse:
    """Render template or a dependency error page."""
    if templates is None:
        return HTMLResponse(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            content=(
                "<html><head>"
                "<link rel='icon' type='image/png' href='/static/favicon.png'>"
                "<link rel='apple-touch-icon' href='/static/favicon.png'>"
                "</head><body><h1>Dashboard dependencies missing</h1>"
                "<p>Install requirements to enable web views: jinja2 and markdown.</p>"
                "</body></html>"
            ),
        )

    return templates.TemplateResponse(
        request=request,
        name=name,
        context=context,
        status_code=status_code,
    )


def _render_error(request: Request, message: str, status_code: int) -> HTMLResponse:
    """Render a friendly dashboard error page."""
    return _render_template(
        request=request,
        name="error.html",
        context={"title": "Dashboard Error", "message": message, "status_code": status_code},
        status_code=status_code,
    )


def _redirect(url: str) -> RedirectResponse:
    """Return a standard POST-redirect-GET response."""
    return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)


def _query_url(path: str, params: dict[str, object | None]) -> str:
    """Build URL with compact query params."""
    query = {key: value for key, value in params.items() if value not in (None, "", False)}
    if not query:
        return path
    return f"{path}?{urlencode(query, doseq=True)}"


def _form_bool(raw: object | None) -> bool:
    """Parse checkbox-style form values into boolean."""
    if raw is None:
        return False
    return str(raw).strip().lower() in {"1", "true", "yes", "on"}


def _parse_metadata(raw: str) -> dict[str, object] | None:
    """Parse metadata JSON object from dashboard form."""
    text = raw.strip()
    if not text:
        return None

    payload = json.loads(text)
    if not isinstance(payload, dict):
        raise ValueError("metadata must be a JSON object")
    return payload


def _latest_engagement_id(db: Session) -> int | None:
    """Return newest engagement id for dashboard shortcut routes."""
    engagements = engagement_service.list_engagements(db)
    if not engagements:
        return None
    return engagements[0].id


async def _read_form_fields(request: Request) -> dict[str, str]:
    """Read URL-encoded form fields without requiring python-multipart."""
    body_raw = (await request.body()).decode("utf-8", errors="replace")
    parsed = parse_qs(body_raw, keep_blank_values=True)
    result: dict[str, str] = {}
    for key, values in parsed.items():
        result[key] = values[-1] if values else ""
    return result


@router.get("/", response_class=HTMLResponse)
def dashboard_home(
    request: Request,
    engagement_id: int | None = Query(default=None, ge=1),
    message: str | None = Query(default=None),
    error: str | None = Query(default=None),
    db: Session = Depends(get_db),
) -> HTMLResponse:
    """Render dashboard home page."""
    try:
        context = dashboard_service.get_home_context(db=db, selected_engagement_id=engagement_id)
    except LookupError as exc:
        return _render_error(request=request, message=str(exc), status_code=status.HTTP_404_NOT_FOUND)

    return _render_template(
        request=request,
        name="dashboard_home.html",
        context={
            "title": f"{settings.app_name} Dashboard",
            "page": "operations",
            "phase_options": [phase.value for phase in EventPhase],
            "severity_options": [level.value for level in SeverityLevel],
            "message": message,
            "error": error,
            **context,
        },
    )


@router.post("/dashboard/engagements/create", response_class=HTMLResponse)
async def dashboard_create_engagement(
    request: Request,
    db: Session = Depends(get_db),
) -> RedirectResponse:
    """Create engagement from dashboard form and redirect to detail page."""
    form = await _read_form_fields(request)
    name = str(form.get("name", "")).strip()
    description = str(form.get("description", "")).strip() or None
    metadata_raw = str(form.get("metadata", "")).strip()

    if not name:
        return _redirect(_query_url("/", {"error": "Engagement name is required"}))

    try:
        metadata = _parse_metadata(metadata_raw)
        payload = EngagementCreate(
            name=name,
            description=description,
            metadata=metadata,
        )
        engagement = engagement_service.create_engagement(db=db, payload=payload)
    except json.JSONDecodeError:
        return _redirect(_query_url("/", {"error": "Metadata must be valid JSON"}))
    except (ValidationError, ValueError) as exc:
        return _redirect(_query_url("/", {"error": str(exc)}))

    return _redirect(
        _query_url(
            f"/dashboard/engagement/{engagement.id}",
            {"message": "Engagement created successfully"},
        ),
    )


@router.post("/dashboard/events/create", response_class=HTMLResponse)
async def dashboard_create_event(
    request: Request,
    db: Session = Depends(get_db),
) -> RedirectResponse:
    """Create event from dashboard form and return to engagement detail."""
    form = await _read_form_fields(request)
    engagement_id_raw = str(form.get("engagement_id", "")).strip()
    try:
        engagement_id = int(engagement_id_raw)
    except ValueError:
        return _redirect(_query_url("/", {"error": "A valid engagement id is required for event creation"}))

    tags_raw = str(form.get("tags", "")).strip()
    evidence_raw = str(form.get("evidence_paths", "")).strip()
    tags = [item.strip() for item in tags_raw.split(",") if item.strip()]

    evidence_paths = [line.strip() for line in evidence_raw.replace(",", "\n").splitlines() if line.strip()]
    if not evidence_paths:
        evidence_paths = None

    try:
        payload = EventCreate(
            engagement_id=engagement_id,
            phase=str(form.get("phase", "")).strip(),
            severity=str(form.get("severity", "")).strip(),
            source=str(form.get("source", "")).strip() or "manual",
            title=str(form.get("title", "")).strip(),
            content=str(form.get("content", "")).strip(),
            tags=tags,
            evidence_paths=evidence_paths,
        )
        event_service.create_event(db=db, payload=payload)
    except (ValidationError, ValueError, LookupError) as exc:
        return _redirect(
            _query_url(
                f"/dashboard/engagement/{engagement_id}",
                {"error": str(exc)},
            ),
        )

    return _redirect(
        _query_url(
            f"/dashboard/engagement/{engagement_id}",
            {"message": "Event added to timeline"},
        ),
    )


@router.get("/dashboard/engagement/{engagement_id}", response_class=HTMLResponse)
def dashboard_engagement_detail(
    request: Request,
    engagement_id: int,
    run_ai: bool = Query(default=False),
    timeline_mode: str = Query(default="chronological", pattern="^(chronological|grouped)$"),
    message: str | None = Query(default=None),
    error: str | None = Query(default=None),
    db: Session = Depends(get_db),
) -> HTMLResponse:
    """Render one engagement detail dashboard."""
    try:
        context = dashboard_service.get_engagement_context(
            db=db,
            engagement_id=engagement_id,
            run_ai=run_ai,
            timeline_mode=timeline_mode,
        )
    except LookupError as exc:
        return _render_error(request=request, message=str(exc), status_code=status.HTTP_404_NOT_FOUND)

    return _render_template(
        request=request,
        name="dashboard_engagement.html",
        context={
            "title": f"Engagement #{engagement_id}",
            "page": "operations",
            "phase_options": [phase.value for phase in EventPhase],
            "severity_options": [level.value for level in SeverityLevel],
            "message": message,
            "error": error,
            **context,
        },
    )


@router.get("/dashboard/report", response_class=HTMLResponse)
def dashboard_report_latest(
    db: Session = Depends(get_db),
) -> RedirectResponse:
    """Open report page for the latest engagement from nav shortcut."""
    engagement_id = _latest_engagement_id(db)
    if engagement_id is None:
        return _redirect(_query_url("/", {"error": "Create an engagement before opening Reporting"}))
    return _redirect(f"/dashboard/report/{engagement_id}")


@router.post("/dashboard/report/{engagement_id}/generate", response_class=HTMLResponse)
async def dashboard_report_generate(
    request: Request,
    engagement_id: int,
) -> RedirectResponse:
    """Apply report options and redirect to report preview."""
    form = await _read_form_fields(request)
    include_vault = _form_bool(form.get("include_vault"))
    include_ai = _form_bool(form.get("include_ai"))
    vault_query = str(form.get("vault_query", "")).strip()

    return _redirect(
        _query_url(
            f"/dashboard/report/{engagement_id}",
            {
                "include_vault": "1" if include_vault else None,
                "include_ai": "1" if include_ai else "0",
                "vault_query": vault_query or None,
                "message": "Report preview updated",
            },
        ),
    )


@router.get("/dashboard/report/{engagement_id}", response_class=HTMLResponse)
def dashboard_report_view(
    request: Request,
    engagement_id: int,
    include_vault: bool = Query(default=False),
    include_ai: bool | None = Query(default=None),
    vault_query: str | None = Query(default=None, max_length=600),
    message: str | None = Query(default=None),
    error: str | None = Query(default=None),
    db: Session = Depends(get_db),
) -> HTMLResponse:
    """Render generated markdown report as HTML."""
    try:
        context = dashboard_service.get_report_context(
            db=db,
            engagement_id=engagement_id,
            include_vault=include_vault,
            include_ai=include_ai,
            vault_query=vault_query,
        )
    except LookupError as exc:
        return _render_error(request=request, message=str(exc), status_code=status.HTTP_404_NOT_FOUND)

    return _render_template(
        request=request,
        name="dashboard_report.html",
        context={
            "title": f"Report #{engagement_id}",
            "page": "reporting",
            "message": message,
            "error": error,
            **context,
        },
    )


@router.get("/dashboard/report/{engagement_id}/download", response_model=None)
def dashboard_report_download(
    request: Request,
    engagement_id: int,
    include_vault: bool = Query(default=False),
    include_ai: bool | None = Query(default=None),
    vault_query: str | None = Query(default=None, max_length=600),
    db: Session = Depends(get_db),
) -> Response:
    """Generate and download markdown report file."""
    try:
        report_relative = report_generator.generate_report(
            db=db,
            engagement_id=engagement_id,
            include_vault=include_vault,
            include_ai=include_ai,
            vault_query=vault_query,
        )
    except LookupError as exc:
        return _render_error(request=request, message=str(exc), status_code=status.HTTP_404_NOT_FOUND)

    report_path = (PROJECT_ROOT / report_relative).resolve()
    if not report_path.exists():
        return _render_error(request=request, message="Generated report file could not be found", status_code=500)

    return FileResponse(path=report_path, media_type="text/markdown", filename=report_path.name)


@router.get("/dashboard/notes", response_class=HTMLResponse)
def dashboard_notes_latest(
    db: Session = Depends(get_db),
) -> RedirectResponse:
    """Open notes page for the latest engagement from nav shortcut."""
    engagement_id = _latest_engagement_id(db)
    if engagement_id is None:
        return _redirect(_query_url("/", {"error": "Create an engagement before opening Notes"}))
    return _redirect(f"/dashboard/notes/{engagement_id}")


@router.post("/dashboard/notes/{engagement_id}/generate", response_class=HTMLResponse)
async def dashboard_notes_generate(
    request: Request,
    engagement_id: int,
    db: Session = Depends(get_db),
) -> RedirectResponse:
    """Generate or append notes from dashboard form."""
    form = await _read_form_fields(request)
    include_vault = _form_bool(form.get("include_vault"))
    vault_query = str(form.get("vault_query", "")).strip() or None

    try:
        notes_generator.generate_notes(
            db=db,
            engagement_id=engagement_id,
            include_vault=include_vault,
            vault_query=vault_query,
        )
    except LookupError as exc:
        return _redirect(_query_url(f"/dashboard/notes/{engagement_id}", {"error": str(exc)}))

    return _redirect(
        _query_url(
            f"/dashboard/notes/{engagement_id}",
            {
                "message": "Notes updated",
                "include_vault": "1" if include_vault else None,
                "vault_query": vault_query,
            },
        ),
    )


@router.get("/dashboard/notes/{engagement_id}", response_class=HTMLResponse)
def dashboard_notes_view(
    request: Request,
    engagement_id: int,
    include_vault: bool = Query(default=False),
    vault_query: str | None = Query(default=None, max_length=600),
    message: str | None = Query(default=None),
    error: str | None = Query(default=None),
    db: Session = Depends(get_db),
) -> HTMLResponse:
    """Render notes preview page."""
    try:
        context = dashboard_service.get_notes_context(db=db, engagement_id=engagement_id)
    except LookupError as exc:
        return _render_error(request=request, message=str(exc), status_code=status.HTTP_404_NOT_FOUND)

    return _render_template(
        request=request,
        name="dashboard_notes.html",
        context={
            "title": f"Notes #{engagement_id}",
            "page": "notes",
            "include_vault": include_vault,
            "vault_query": vault_query or "",
            "message": message,
            "error": error,
            **context,
        },
    )


@router.get("/dashboard/tasks", response_class=HTMLResponse)
def dashboard_tasks(
    request: Request,
    view: str = Query(default="all"),
    show_completed: bool = Query(default=False),
) -> HTMLResponse:
    """Render aggregated tasks view across enabled providers."""
    context = dashboard_service.get_tasks_context(task_view=view, show_completed=show_completed)
    return _render_template(
        request=request,
        name="dashboard_tasks.html",
        context={
            "title": "Tasks",
            "page": "tasks",
            **context,
        },
    )


@router.get("/dashboard/settings", response_class=HTMLResponse)
@router.get("/dashboard/config", response_class=HTMLResponse)
def dashboard_config(request: Request) -> HTMLResponse:
    """Render dashboard config shell using shared configuration UI."""
    return _render_template(
        request=request,
        name="dashboard_settings.html",
        context={
            "title": "Config",
            "page": "config",
            "config_embed_url": "/config?embedded=dashboard",
        },
    )


@router.get("/dashboard/vault", response_class=HTMLResponse)
def dashboard_vault(
    request: Request,
    message: str | None = Query(default=None),
    error: str | None = Query(default=None),
) -> HTMLResponse:
    """Render Knowledge Vault management page."""
    return _render_template(
        request=request,
        name="dashboard_vault.html",
        context={
            "title": "Vault",
            "page": "vault",
            "message": message,
            "error": error,
        },
    )
