import type { FastifyInstance } from "fastify";
import { z } from "zod";
import * as cp from "../../repos/counterparties.js";
import { writeAudit } from "../../audit/audit.js";
import type { CounterpartyRow } from "../../repos/counterparties.js";

/** Rotas de contrapartes/clientes. PII só é revelada para admin (auditado). */

const idParams = z.object({ id: z.coerce.number().int().positive() });

/** Visão sem PII (segura para listagem). */
function toPublic(row: CounterpartyRow) {
  return {
    id: row.id,
    exchange: row.exchange,
    counterpartyRef: row.counterparty_ref,
    displayName: row.display_name,
    validated: !!row.validated,
    totalOrders: row.total_orders,
    hasCpf: row.cpf_enc != null,
    hasPhone: row.phone_enc != null,
    hasPix: row.pix_key_enc != null,
  };
}

export async function counterpartyRoutes(app: FastifyInstance): Promise<void> {
  const auth = { preHandler: app.authenticate };
  const adminOnly = { preHandler: app.requireRole("admin") };

  app.get("/counterparties", auth, async () => {
    const rows = await cp.list();
    return rows.map(toPublic);
  });

  app.get("/counterparties/:id", auth, async (req, reply) => {
    const { id } = idParams.parse(req.params);
    const row = await cp.getById(id);
    if (!row) return reply.code(404).send({ error: "Contraparte não encontrada." });
    return toPublic(row);
  });

  // Revela a PII decifrada — admin + auditado (LGPD).
  app.get("/counterparties/:id/pii", adminOnly, async (req, reply) => {
    const { id } = idParams.parse(req.params);
    const row = await cp.getById(id);
    if (!row) return reply.code(404).send({ error: "Contraparte não encontrada." });
    await writeAudit({ userId: req.user.sub, action: "counterparty.reveal_pii", entity: "counterparty", entityId: id });
    return cp.decryptPII(row);
  });

  // Marca como validada e grava CPF/telefone/PIX (criptografados).
  app.post("/counterparties/:id/validate", auth, async (req, reply) => {
    const { id } = idParams.parse(req.params);
    const body = z
      .object({
        cpf: z.string().nullish(),
        phone: z.string().nullish(),
        pixKey: z.string().nullish(),
      })
      .safeParse(req.body);
    if (!body.success) return reply.code(400).send({ error: "Dados inválidos." });
    if (!(await cp.getById(id))) return reply.code(404).send({ error: "Contraparte não encontrada." });

    await cp.setValidated(id, {
      cpf: body.data.cpf ?? null,
      phone: body.data.phone ?? null,
      pixKey: body.data.pixKey ?? null,
    });
    await writeAudit({ userId: req.user.sub, action: "counterparty.validate", entity: "counterparty", entityId: id });
    const row = await cp.getById(id);
    return row ? toPublic(row) : reply.code(404).send({ error: "Contraparte não encontrada." });
  });
}
