"""API 라우트."""
from __future__ import annotations

import base64
from typing import Any

from fastapi import APIRouter, File, HTTPException, UploadFile
from fastapi.responses import FileResponse, HTMLResponse
from pydantic import BaseModel

from app.config import settings
from app.export.harness import SUPPORTED, export_pages, export_report
from app.services import catalog, pipeline, render_llm
from app.services import demo as demo_svc

router = APIRouter(prefix="/api")


@router.get("/health")
def health() -> dict[str, Any]:
    return {
        "status": "ok",
        "model": settings.llm_model,
        "exportFormats": SUPPORTED,
        "demoAvailable": demo_svc.available(),
    }


@router.post("/extract")
async def extract(image: UploadFile = File(...), demo: bool = False) -> dict[str, Any]:  # noqa: A002
    if demo and demo_svc.available():
        return demo_svc.load_extract()
    raw = await image.read()
    if not raw:
        raise HTTPException(400, "빈 이미지입니다.")
    if len(raw) > 10 * 1024 * 1024:
        raise HTTPException(400, "이미지가 10MB를 초과합니다.")
    mime = image.content_type or "image/png"
    b64 = base64.b64encode(raw).decode("ascii")
    try:
        result = pipeline.run_extract(b64, mime)
    except Exception as e:  # noqa: BLE001
        raise HTTPException(500, f"추출 실패: {e}") from e
    return result


@router.get("/catalog")
def get_catalog() -> dict[str, Any]:
    return catalog.load_catalog()


@router.get("/products/search")
def product_search(q: str = "") -> dict[str, Any]:
    return {"results": catalog.search_products(q)}


@router.get("/products/{fund_code}/data")
def product_data(fund_code: str) -> dict[str, Any]:
    data = catalog.get_product(fund_code)
    if data is None:
        raise HTTPException(404, f"상품을 찾을 수 없습니다: {fund_code}")
    return data


class GenerateRequest(BaseModel):
    ir: dict[str, Any]
    fundCode: str
    demo: bool = False


@router.post("/generate")
def generate(req: GenerateRequest) -> dict[str, Any]:
    if req.demo and demo_svc.available():
        return demo_svc.load_generate()
    if catalog.get_product(req.fundCode) is None:
        raise HTTPException(404, f"상품을 찾을 수 없습니다: {req.fundCode}")
    try:
        return pipeline.run_generate(req.ir, req.fundCode)
    except Exception as e:  # noqa: BLE001
        raise HTTPException(500, f"생성 실패: {e}") from e


class PreviewRequest(BaseModel):
    view: dict[str, Any]
    backgroundDataUrl: str | None = None
    bgOpacity: float = 1.0
    referenceImageDataUrl: str | None = None
    backgroundImageDataUrl: str | None = None  # 로고/타이틀/푸터가 든 페이지 배경
    mode: str = "llm"  # 항상 LLM 으로 원본 레이아웃 재현


@router.post("/preview", response_class=HTMLResponse)
def preview(req: PreviewRequest) -> HTMLResponse:
    if not req.referenceImageDataUrl:
        raise HTTPException(400, "원본 이미지가 없어 레이아웃을 재현할 수 없습니다. 이미지를 업로드해 주세요.")
    try:
        html, _ = render_llm.render_html_llm_from_dataurl(
            req.view, req.referenceImageDataUrl, req.backgroundImageDataUrl
        )
        return HTMLResponse(html)
    except Exception as e:  # noqa: BLE001
        raise HTTPException(500, f"LLM HTML 생성 실패: {e}") from e


class PageItem(BaseModel):
    view: dict[str, Any]
    backgroundDataUrl: str | None = None
    bgOpacity: float = 1.0
    referenceImageDataUrl: str | None = None
    backgroundImageDataUrl: str | None = None  # 로고/타이틀/푸터가 든 페이지 배경


class PreviewPagesRequest(BaseModel):
    pages: list[PageItem]
    mode: str = "llm"  # 항상 LLM 으로 원본 레이아웃 재현
    demo: bool = False


@router.post("/preview-pages", response_class=HTMLResponse)
def preview_pages(req: PreviewPagesRequest) -> HTMLResponse:
    if req.demo and len(req.pages) == 1:
        cached = demo_svc.load_preview()
        if cached is not None:
            return HTMLResponse(cached)
    pages = [p.model_dump() for p in req.pages]
    if not any(p.get("referenceImageDataUrl") for p in pages):
        raise HTTPException(400, "원본 이미지가 없어 레이아웃을 재현할 수 없습니다. 이미지를 업로드해 주세요.")
    try:
        html, _ = render_llm.render_pages_html_llm(pages)
        return HTMLResponse(html)
    except Exception as e:  # noqa: BLE001
        raise HTTPException(500, f"LLM HTML 생성 실패: {e}") from e


class ExportRequest(BaseModel):
    format: str
    view: dict[str, Any]
    backgroundDataUrl: str | None = None
    bgOpacity: float = 1.0
    referenceImageDataUrl: str | None = None
    backgroundImageDataUrl: str | None = None  # 로고/타이틀/푸터가 든 페이지 배경
    demo: bool = False


@router.post("/export")
def export(req: ExportRequest) -> dict[str, Any]:
    fmt = req.format.lower()
    if fmt not in SUPPORTED:
        raise HTTPException(400, f"지원하지 않는 포맷: {req.format}")
    if req.demo and demo_svc.export_file(fmt) is not None:
        p = demo_svc.export_file(fmt)
        return {
            "format": fmt,
            "filename": f"Samsung_ETF_Report.{fmt}",
            "size": p.stat().st_size,
            "url": f"/api/demo-download/{fmt}",
        }
    try:
        return export_report(
            req.format, req.view, req.backgroundDataUrl, req.bgOpacity,
            req.referenceImageDataUrl, req.backgroundImageDataUrl,
        )
    except Exception as e:  # noqa: BLE001
        raise HTTPException(500, f"{req.format} export 실패: {e}") from e


class ExportPagesRequest(BaseModel):
    format: str
    pages: list[PageItem]
    demo: bool = False


@router.post("/export-pages")
def export_pages_route(req: ExportPagesRequest) -> dict[str, Any]:
    fmt = req.format.lower()
    if fmt not in SUPPORTED:
        raise HTTPException(400, f"지원하지 않는 포맷: {req.format}")
    if req.demo and demo_svc.export_file(fmt) is not None:
        p = demo_svc.export_file(fmt)
        return {
            "format": fmt,
            "filename": f"Samsung_ETF_Report.{fmt}",
            "size": p.stat().st_size,
            "url": f"/api/demo-download/{fmt}",
            "pages": len(req.pages),
        }
    try:
        pages = [p.model_dump() for p in req.pages]
        result = export_pages(req.format, pages)
        result["pages"] = len(pages)
        return result
    except Exception as e:  # noqa: BLE001
        raise HTTPException(500, f"{req.format} export 실패: {e}") from e


@router.get("/demo-download/{fmt}")
def demo_download(fmt: str):
    p = demo_svc.export_file(fmt.lower())
    if p is None:
        raise HTTPException(404, "데모 파일이 없습니다.")
    return FileResponse(str(p), filename=f"Samsung_ETF_Report.{fmt.lower()}")


@router.get("/download/{filename}")
def download(filename: str):
    path = (settings.output_path / filename).resolve()
    if not str(path).startswith(str(settings.output_path)) or not path.exists():
        raise HTTPException(404, "파일을 찾을 수 없습니다.")
    return FileResponse(str(path), filename=filename)


class SampleBboxRequest(BaseModel):
    bboxes: dict[str, dict[str, float]]


@router.post("/demo/sample-bboxes")
def save_sample_bboxes(req: SampleBboxRequest) -> dict[str, Any]:
    """[임시] 데모 샘플 박스 위치 보정값을 픽스처에 저장."""
    if not demo_svc.available():
        raise HTTPException(400, "데모 픽스처가 없습니다.")
    updated = demo_svc.save_sample_bboxes(req.bboxes)
    return {"updated": updated}
