import { Hono } from "hono";
import { requirePermissions, unifiedAuthMiddleware } from "@/middleware/auth";
import { projectContextMiddleware } from "@/middleware/project-context";
import type { Env } from "@/types/env";
import {
  addAllowlistEntry,
  listAllowlist,
  listAllowlistLabels,
  removeAllowlistEntry,
} from "./handlers/allowlist";
import { getAssetAuditHistory } from "./handlers/audit";
import { executeUpdateAuthority, prepareUpdateAuthority } from "./handlers/authority";
import { executeBurn, prepareBurn } from "./handlers/burn";
import {
  confirmDeploy,
  deployToken,
  prepareDeploy,
  prepareDeployMetadata,
} from "./handlers/deploy";
import { executeForceBurn, prepareForceBurn } from "./handlers/force-burn";
import { freezeAccount, listFrozenAccounts, unfreezeAccount } from "./handlers/freeze";
import { serveTokenMetadata } from "./handlers/metadata";
import { executeMint, prepareMint } from "./handlers/mint";
import { pauseToken, unpauseToken } from "./handlers/pause";
import { executeSeize, prepareSeize } from "./handlers/seize";
import { refreshTokenSupply } from "./handlers/supply";
import { getTokenTemplate, listTokenTemplates } from "./handlers/templates";
import { createToken, getToken, listTokenFacets, listTokens, updateToken } from "./handlers/tokens";
import { listTokenTransactions, listTransactions } from "./handlers/transactions";

const issuance = new Hono<{ Bindings: Env }>();

// Public: SDP-hosted token metadata JSON. Registered BEFORE the auth middleware
// below so wallets and explorers can fetch it without credentials (Hono applies
// `use(...)` only to routes registered after it). App-wide KV/rate-limit bypass
// for this path is wired via KV_FREE_PATHS in app.ts.
issuance.get("/tokens/:tokenId/metadata.json", serveTokenMetadata);

issuance.use("*", unifiedAuthMiddleware({ allowClerk: true, allowSession: true }));
issuance.use("*", projectContextMiddleware());

// Templates (read-only, any authenticated user can view)
issuance.get("/templates", requirePermissions("tokens:read"), listTokenTemplates);
issuance.get("/templates/:templateId", requirePermissions("tokens:read"), getTokenTemplate);

// Token CRUD
issuance.post("/tokens", requirePermissions("tokens:write"), createToken);
issuance.get("/tokens", requirePermissions("tokens:read"), listTokens);
issuance.get("/transactions", requirePermissions("tokens:read"), listTransactions);
// Filter facets for the token list. Registered BEFORE `/tokens/:tokenId` so the
// literal path wins the match instead of being read as a token id.
issuance.get("/tokens/facets", requirePermissions("tokens:read"), listTokenFacets);
issuance.get("/tokens/:tokenId", requirePermissions("tokens:read"), getToken);
issuance.get(
  "/tokens/:tokenId/transactions",
  requirePermissions("tokens:read"),
  listTokenTransactions
);
issuance.get("/tokens/:tokenId/audit", requirePermissions("tokens:read"), getAssetAuditHistory);
issuance.post(
  "/tokens/:tokenId/supply/refresh",
  requirePermissions("tokens:read"),
  refreshTokenSupply
);
issuance.patch("/tokens/:tokenId", requirePermissions("tokens:write"), updateToken);

// Deploy
issuance.post("/tokens/:tokenId/deploy", requirePermissions("tokens:write"), deployToken);
issuance.post("/tokens/:tokenId/deploy/prepare", requirePermissions("tokens:write"), prepareDeploy);
// Confirmation step for the non-custodial deploy flow: records the mint after
// the client signs+submits the prepared create tx. Re-derives authorities from
// the signing wallet prepareDeploy persisted on the token, so they can't diverge
// from the prepared tx. Required before prepare-metadata can run.
issuance.post("/tokens/:tokenId/deploy/confirm", requirePermissions("tokens:write"), confirmDeploy);
// Follow-up tx for the non-custodial deploy flow: set the metadata uri when the
// create tx had to be prepared with an empty uri to stay under the packet limit.
issuance.post(
  "/tokens/:tokenId/deploy/prepare-metadata",
  requirePermissions("tokens:write"),
  prepareDeployMetadata
);

// Mint
issuance.post("/tokens/:tokenId/mint/prepare", requirePermissions("tokens:write"), prepareMint);
issuance.post("/tokens/:tokenId/mint", requirePermissions("tokens:write"), executeMint);

// Burn
issuance.post("/tokens/:tokenId/burn/prepare", requirePermissions("tokens:write"), prepareBurn);
issuance.post("/tokens/:tokenId/burn", requirePermissions("tokens:write"), executeBurn);

// Seize (Force Transfer)
issuance.post("/tokens/:tokenId/seize/prepare", requirePermissions("tokens:admin"), prepareSeize);
issuance.post("/tokens/:tokenId/seize", requirePermissions("tokens:admin"), executeSeize);

// Force Burn
issuance.post(
  "/tokens/:tokenId/force-burn/prepare",
  requirePermissions("tokens:admin"),
  prepareForceBurn
);
issuance.post("/tokens/:tokenId/force-burn", requirePermissions("tokens:admin"), executeForceBurn);

// Authority Updates
issuance.post(
  "/tokens/:tokenId/authority/prepare",
  requirePermissions("tokens:admin"),
  prepareUpdateAuthority
);
issuance.post(
  "/tokens/:tokenId/authority",
  requirePermissions("tokens:admin"),
  executeUpdateAuthority
);

// Pause/Unpause
issuance.post("/tokens/:tokenId/pause", requirePermissions("tokens:admin"), pauseToken);
issuance.post("/tokens/:tokenId/unpause", requirePermissions("tokens:admin"), unpauseToken);

// Freeze/Unfreeze
issuance.post("/tokens/:tokenId/freeze", requirePermissions("tokens:admin"), freezeAccount);
issuance.post("/tokens/:tokenId/unfreeze", requirePermissions("tokens:admin"), unfreezeAccount);
issuance.get("/tokens/:tokenId/frozen", requirePermissions("tokens:read"), listFrozenAccounts);

// Allowlist
// `/allowlist/labels` (GET) is registered before the `/allowlist/:entryId`
// (DELETE) route; distinct methods mean there is no path collision either way.
issuance.get(
  "/tokens/:tokenId/allowlist/labels",
  requirePermissions("tokens:read"),
  listAllowlistLabels
);
issuance.get("/tokens/:tokenId/allowlist", requirePermissions("tokens:read"), listAllowlist);
issuance.post("/tokens/:tokenId/allowlist", requirePermissions("tokens:write"), addAllowlistEntry);
issuance.delete(
  "/tokens/:tokenId/allowlist/:entryId",
  requirePermissions("tokens:write"),
  removeAllowlistEntry
);

export default issuance;
