import { NextResponse } from "next/server"
import { z } from "zod"
import { ConvexHttpClient } from "convex/browser"

import { api } from "@/convex/_generated/api"
import { env } from "@/lib/env"

const fleetHostSchema = z.object({
  host: z
    .object({
      id: z.number().optional(),
      hostname: z.string().optional(),
      display_name: z.string().optional(),
      platform: z.string().optional(),
      os_version: z.string().optional(),
      hardware_model: z.string().optional(),
      hardware_serial: z.string().optional(),
      hardware_uuid: z.string().optional(),
      uuid: z.string().optional(),
      device_id: z.string().optional(),
      primary_ip: z.string().optional(),
      public_ip: z.string().optional(),
      primary_mac: z.string().optional(),
      macs: z.string().optional(),
      serial_number: z.string().optional(),
      memory: z.number().optional(),
      cpu_type: z.string().optional(),
      cpu_physical_cores: z.number().optional(),
      cpu_logical_cores: z.number().optional(),
      hardware_vendor: z.string().optional(),
      computer_name: z.string().optional(),
      detail_updated_at: z.string().optional(),
      platform_like: z.string().optional(),
      osquery_version: z.string().optional(),
      team_id: z.number().optional(),
      software: z
        .array(
          z.object({
            name: z.string().optional(),
            version: z.string().optional(),
            source: z.string().optional(),
          })
        )
        .optional(),
      labels: z
        .array(
          z.object({
            id: z.number(),
            name: z.string(),
          })
        )
        .optional(),
    })
    .transform((value) => value ?? {}),
})

function extractMacs(host: z.infer<typeof fleetHostSchema>["host"]) {
  const macs = new Set<string>()
  const append = (input?: string | null) => {
    if (!input) return
    input
      .split(/[\s,]+/)
      .map((mac) => mac.trim())
      .filter(Boolean)
      .forEach((mac) => macs.add(mac))
  }
  append(host.primary_mac)
  append(host.macs)
  return Array.from(macs)
}

function extractSerials(host: z.infer<typeof fleetHostSchema>["host"]) {
  return [
    host.hardware_serial,
    host.hardware_uuid,
    host.uuid,
    host.serial_number,
    host.device_id,
  ]
    .map((value) => value?.trim())
    .filter((value): value is string => Boolean(value))
}

export async function POST(request: Request) {
  const fleetSecret = env.FLEET_SYNC_SECRET ?? env.MACHINE_PROVISIONING_SECRET
  if (!fleetSecret) {
    return NextResponse.json({ error: "Sincronização Fleet não configurada" }, { status: 500 })
  }

  const providedSecret = request.headers.get("x-fleet-secret") ?? request.headers.get("authorization")?.replace(/^Bearer\s+/i, "")
  if (!providedSecret || providedSecret !== fleetSecret) {
    return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
  }

  const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
  if (!convexUrl) {
    return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
  }

  let parsed
  try {
    const raw = await request.json()
    parsed = fleetHostSchema.parse(raw)
  } catch (error) {
    return NextResponse.json({ error: "Payload inválido", details: error instanceof Error ? error.message : String(error) }, { status: 400 })
  }

  const host = parsed.host
  const hostname = host.hostname ?? host.computer_name ?? host.display_name
  if (!hostname) {
    return NextResponse.json({ error: "Host sem hostname válido" }, { status: 400 })
  }

  const macAddresses = extractMacs(host)
  const serialNumbers = extractSerials(host)

  if (macAddresses.length === 0 && serialNumbers.length === 0) {
    return NextResponse.json({ error: "Host sem identificadores de hardware (MAC ou serial)" }, { status: 400 })
  }

  const osInfo = {
    name: host.os_version ?? host.platform ?? "desconhecido",
    version: host.os_version,
    architecture: host.platform_like,
  }

  const inventory = {
    fleet: {
      id: host.id,
      teamId: host.team_id,
      detailUpdatedAt: host.detail_updated_at,
      osqueryVersion: host.osquery_version,
    },
    hardware: {
      vendor: host.hardware_vendor,
      model: host.hardware_model,
      serial: host.hardware_serial ?? host.serial_number,
      cpuType: host.cpu_type,
      physicalCores: host.cpu_physical_cores,
      logicalCores: host.cpu_logical_cores,
      memoryBytes: host.memory,
    },
    network: {
      primaryIp: host.primary_ip,
      publicIp: host.public_ip,
      macAddresses,
    },
    labels: host.labels,
    software: host.software?.slice(0, 50).map((item) => ({
      name: item.name,
      version: item.version,
      source: item.source,
    })),
  }

  const metrics = {
    memoryBytes: host.memory,
    cpuPhysicalCores: host.cpu_physical_cores,
    cpuLogicalCores: host.cpu_logical_cores,
  }

  const client = new ConvexHttpClient(convexUrl)

  try {
    const result = await client.mutation(api.machines.upsertInventory, {
      provisioningCode: fleetSecret,
      hostname,
      os: osInfo,
      macAddresses,
      serialNumbers,
      inventory,
      metrics,
      registeredBy: "fleet",
    })

    return NextResponse.json({ ok: true, machineId: result.machineId, status: result.status })
  } catch (error) {
    console.error("[fleet.hosts] Falha ao sincronizar inventário", error)
    return NextResponse.json({ error: "Falha ao sincronizar inventário" }, { status: 500 })
  }
}
