import { randomUUID } from "node:crypto"
import type { FastifyPluginAsyncZod } from "@fastify/type-provider-zod"
import {
  certificateIssueRequestSchema,
  putAcmeCloudflareSettingsSchema,
  putCertificateRenewSettingsSchema,
  testAcmeCloudflareSettingsSchema,
} from "@mmapp/contracts/certificates"
import { refreshScheduler } from "../services/scheduler.js"
import { testCloudflareToken } from "../services/acme-cloudflare.js"
import { queueCertificateIssueJob } from "../services/certificate-issue-runner.js"
import {
  createIssueJobRecord,
  getAcmeCloudflareToken,
  getAcmeSettingsPublic,
  getCertificateRenewSettings,
  getIssueJobRecord,
  getServerRowByIdString,
  listCertificatesFromServers,
  toIssueJobDto,
  updateAcmeSettings,
  updateCertificateRenewSettings,
} from "../services/certificates-service.js"

const certificatesRoutes: FastifyPluginAsyncZod = async (app) => {
  app.get("/certificates", async (_req, reply) => {
    return reply.send(await listCertificatesFromServers())
  })

  app.post("/certificates/refresh", async (_req, reply) => {
    return reply.send(await listCertificatesFromServers())
  })

  app.get("/certificates/acme-settings", async (_req, reply) => {
    return reply.send(getAcmeSettingsPublic())
  })

  app.put("/certificates/acme-settings", async (req, reply) => {
    const parsed = putAcmeCloudflareSettingsSchema.safeParse(req.body ?? {})
    if (!parsed.success) {
      return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
    }
    return reply.send(updateAcmeSettings(parsed.data))
  })

  app.get("/certificates/renew-settings", async (_req, reply) => {
    return reply.send(getCertificateRenewSettings())
  })

  app.put("/certificates/renew-settings", async (req, reply) => {
    const parsed = putCertificateRenewSettingsSchema.safeParse(req.body ?? {})
    if (!parsed.success) {
      return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
    }
    const result = updateCertificateRenewSettings(parsed.data)
    refreshScheduler()
    return reply.send(result)
  })

  app.post("/certificates/acme-settings/test", async (req, reply) => {
    const parsed = testAcmeCloudflareSettingsSchema.safeParse(req.body ?? {})
    if (!parsed.success) {
      return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
    }
    const effective = parsed.data.cloudflareApiToken?.trim() || getAcmeCloudflareToken()
    if (!effective) {
      return reply.status(400).send({ error: "Не задан Cloudflare API token" })
    }
    try {
      await testCloudflareToken(effective)
      return reply.send({ ok: true, message: "Cloudflare API доступен" })
    } catch (e) {
      return reply.status(400).send({
        ok: false,
        message: e instanceof Error ? e.message : String(e),
      })
    }
  })

  app.post("/certificates/issue", async (req, reply) => {
    const parsed = certificateIssueRequestSchema.safeParse(req.body ?? {})
    if (!parsed.success) {
      return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
    }
    const serverId = String(parsed.data.serverId)
    const server = getServerRowByIdString(serverId)
    if (!server) return reply.status(404).send({ error: "Сервер не найден" })

    const jobId = randomUUID()
    const trustStore = (parsed.data.trustStore ?? ["www", "api"]).join(",")
    createIssueJobRecord({
      id: jobId,
      serverId,
      certName: parsed.data.certName.trim(),
      domainNames: parsed.data.domainNames.map((d) => d.trim()).filter(Boolean),
      keyType: parsed.data.keyType ?? "rsa2048",
      trustStore,
      source: "manual",
    })
    queueCertificateIssueJob(jobId)
    return reply.send({ jobId })
  })

  app.get("/certificates/issue/:jobId", async (req, reply) => {
    const jobId = String((req.params as { jobId: string }).jobId)
    const row = getIssueJobRecord(jobId)
    if (!row) return reply.status(404).send({ error: "Задача не найдена" })
    return reply.send(toIssueJobDto(row))
  })
}

export default certificatesRoutes
