"""Servicio HTTP local del xamiSDK.

Dos superficies:
  1. /receive  -> el UNICO endpoint por el que Xami empuja (sign.result, config...).
                  El sobre (JWE+JWS) lo procesa el MessageRouter.
  2. /v1/*      -> API LOCAL para los wrappers (PHP/Node/Java), autenticada con la
                  credencial de wrapper (wrapper_key + secret). Firmar, consultar
                  cache y resultados.

El servicio corre en la infra del cliente. El documento NUNCA sale: sign() sube el
PDF al servicio LOCAL, que hace el hash local y solo envia el hash a Xami.

Ver docs/arquitectura_xamiSDK.md y docs/wrapper_xamiSDK_php.md.
"""
from __future__ import annotations
import base64
import logging

from fastapi import FastAPI, Request, HTTPException, Header, Depends
from pydantic import BaseModel

from xamisdk.config import Config
from xamisdk.router import MessageRouter, RouterError
from xamisdk.signer import Signer, SignerError
from xamisdk.cache import Cache
from xamisdk.results import Results
from xamisdk.wrappers import Wrappers

log = logging.getLogger("xamisdk.service")


class SignRequest(BaseModel):
    pdf_base64: str
    credential_key: str
    design_key: str | None = None
    variables: dict | None = None
    reason: str | None = None
    location: str | None = None
    signer_name: str | None = None


class BlockchainSignRequest(BaseModel):
    tx_hash: str
    credential_key: str
    chain_id: int | None = None


def create_app(config: Config | None = None) -> FastAPI:
    cfg = config or Config()
    app = FastAPI(title="xamiSDK local", docs_url=None, redoc_url=None)

    router = MessageRouter(cfg)
    signer = Signer(cfg)
    cache = Cache(cfg.base_dir)
    results = Results(cfg.base_dir)
    wrappers = Wrappers(cfg.base_dir)

    # ---- auth local de wrapper ----
    def require_wrapper(x_wrapper_key: str = Header(None),
                        x_wrapper_secret: str = Header(None)) -> dict:
        if not x_wrapper_key or not x_wrapper_secret:
            raise HTTPException(401, "faltan X-Wrapper-Key / X-Wrapper-Secret")
        w = wrappers.verify(x_wrapper_key, x_wrapper_secret)
        if not w:
            raise HTTPException(401, "credencial de wrapper invalida o revocada")
        return w

    # ---- 1. recepcion del push de Xami ----
    @app.post("/receive")
    async def receive(request: Request):
        body = (await request.body()).decode()
        try:
            res = router.handle_envelope(body)
        except RouterError as e:
            raise HTTPException(400, f"sobre invalido: {e}")
        return res

    # ---- 2. API local para wrappers ----
    @app.get("/health")
    def health():
        st = cfg.load_state()
        return {"ok": True, "paired": bool(st.get("paired")),
                "tenant_id": st.get("tenant_id"),
                "cache": cache.summary()}

    @app.post("/v1/pades/sign")
    def pades_sign(req: SignRequest, w: dict = Depends(require_wrapper)):
        try:
            pdf_bytes = base64.b64decode(req.pdf_base64)
        except Exception:
            raise HTTPException(422, "pdf_base64 invalido")
        try:
            rid = signer.sign_pdf(pdf_bytes, req.credential_key,
                                  design_key=req.design_key, variables=req.variables,
                                  reason=req.reason, location=req.location,
                                  signer_name=req.signer_name)
        except SignerError as e:
            raise HTTPException(502, str(e))
        return {"request_id": rid, "status": "PENDING"}

    @app.post("/v1/blockchain/sign")
    def blockchain_sign(req: BlockchainSignRequest, w: dict = Depends(require_wrapper)):
        try:
            rid = signer.sign_blockchain(req.tx_hash, req.credential_key, chain_id=req.chain_id)
        except SignerError as e:
            raise HTTPException(502, str(e))
        return {"request_id": rid, "status": "PENDING"}

    @app.get("/v1/results/{request_id}")
    def get_result(request_id: str, w: dict = Depends(require_wrapper)):
        rec = results.get(request_id)
        if not rec:
            raise HTTPException(404, "request_id no encontrado")
        # si esta DONE, incluir el PDF firmado en base64
        if rec.get("status") == "DONE" and rec.get("output_path"):
            try:
                with open(rec["output_path"], "rb") as f:
                    rec = dict(rec)
                    rec["pdf_base64"] = base64.b64encode(f.read()).decode()
            except Exception:
                pass
        return rec

    @app.get("/v1/credentials")
    def list_credentials(w: dict = Depends(require_wrapper)):
        return {"credentials": cache.list_credentials()}

    @app.get("/v1/designs")
    def list_designs(w: dict = Depends(require_wrapper)):
        return {"designs": cache.list_designs()}

    return app


