import { z } from "zod"

import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { ensureCollaboratorAccount, ensureMachineAccount } from "@/server/machines-auth"
import { createCorsPreflight, jsonWithCors } from "@/server/cors"
import { prisma } from "@/lib/prisma"
import { createConvexClient, ConvexConfigurationError } from "@/server/convex-client"

const registerSchema = z
  .object({
    provisioningCode: z.string().min(32),
    hostname: z.string().min(1),
    os: z.object({
      name: z.string().min(1),
      version: z.string().optional(),
      architecture: z.string().optional(),
    }),
    macAddresses: z.array(z.string()).default([]),
    serialNumbers: z.array(z.string()).default([]),
    metadata: z.record(z.string(), z.unknown()).optional(),
    registeredBy: z.string().optional(),
    accessRole: z.enum(["collaborator", "manager"]).optional(),
    collaborator: z
      .object({
        email: z.string().email(),
        name: z.string().min(1, "Informe o nome do colaborador/gestor"),
      })
      .optional(),
  })
  .refine(
    (data) => (data.macAddresses && data.macAddresses.length > 0) || (data.serialNumbers && data.serialNumbers.length > 0),
    { message: "Informe ao menos um MAC address ou número de série" }
  )

const CORS_METHODS = "POST, OPTIONS"

export async function OPTIONS(request: Request) {
  return createCorsPreflight(request.headers.get("origin"), CORS_METHODS)
}

export async function POST(request: Request) {
  const origin = request.headers.get("origin")
  if (request.method !== "POST") {
    return jsonWithCors({ error: "Método não permitido" }, 405, origin, CORS_METHODS)
  }

  let client
  try {
    client = createConvexClient()
  } catch (error) {
    if (error instanceof ConvexConfigurationError) {
      return jsonWithCors({ error: error.message }, 500, origin, CORS_METHODS)
    }
    throw error
  }

  let payload
  try {
    const raw = await request.json()
    payload = registerSchema.parse(raw)
  } catch (error) {
    return jsonWithCors(
      { error: "Payload inválido", details: error instanceof Error ? error.message : String(error) },
      400,
      origin,
      CORS_METHODS
    )
  }

  try {
    const provisioningCode = payload.provisioningCode.trim().toLowerCase()
    const companyRecord = await prisma.company.findFirst({
      where: { provisioningCode },
      select: { id: true, tenantId: true, name: true, slug: true, provisioningCode: true },
    })

    if (!companyRecord) {
      return jsonWithCors(
        { error: "Código de provisionamento inválido" },
        404,
        origin,
        CORS_METHODS
      )
    }

    const tenantId = companyRecord.tenantId ?? DEFAULT_TENANT_ID
    const persona = payload.accessRole ?? undefined
    const collaborator = payload.collaborator ?? null

    if (persona && !collaborator) {
      return jsonWithCors(
        { error: "Informe os dados do colaborador/gestor ao definir o perfil de acesso." },
        400,
        origin,
        CORS_METHODS
      )
    }

    let metadataPayload: Record<string, unknown> | undefined = payload.metadata
      ? { ...(payload.metadata as Record<string, unknown>) }
      : undefined
    if (collaborator) {
      const collaboratorMeta = {
        email: collaborator.email,
        name: collaborator.name ?? null,
        role: persona ?? "collaborator",
      }
      metadataPayload = {
        ...(metadataPayload ?? {}),
        collaborator: collaboratorMeta,
      }
    }

    await client.mutation(api.companies.ensureProvisioned, {
      tenantId,
      slug: companyRecord.slug,
      name: companyRecord.name,
      provisioningCode: companyRecord.provisioningCode,
    })

    const registration = await client.mutation(api.machines.register, {
      provisioningCode,
      hostname: payload.hostname,
      os: payload.os,
      macAddresses: payload.macAddresses,
      serialNumbers: payload.serialNumbers,
      metadata: metadataPayload,
      registeredBy: payload.registeredBy,
    })

    const account = await ensureMachineAccount({
      machineId: registration.machineId,
      tenantId: registration.tenantId ?? DEFAULT_TENANT_ID,
      hostname: payload.hostname,
      machineToken: registration.machineToken,
      persona,
    })

    await client.mutation(api.machines.linkAuthAccount, {
      machineId: registration.machineId as Id<"machines">,
      authUserId: account.authUserId,
      authEmail: account.authEmail,
    })

    let assignedUserId: Id<"users"> | undefined
    if (collaborator) {
      const ensuredUser = (await client.mutation(api.users.ensureUser, {
        tenantId,
        email: collaborator.email,
        name: collaborator.name ?? collaborator.email,
        avatarUrl: undefined,
        role: persona?.toUpperCase(),
        companyId: registration.companyId ? (registration.companyId as Id<"companies">) : undefined,
      })) as { _id?: Id<"users"> } | null

      await ensureCollaboratorAccount({
        email: collaborator.email,
        name: collaborator.name ?? collaborator.email,
        tenantId,
        companyId: companyRecord.id,
        role: persona === "manager" ? "MANAGER" : "COLLABORATOR",
      })

      if (persona) {
        assignedUserId = ensuredUser?._id
        await client.mutation(api.machines.updatePersona, {
          machineId: registration.machineId as Id<"machines">,
          persona,
          ...(assignedUserId ? { assignedUserId } : {}),
          assignedUserEmail: collaborator.email,
          assignedUserName: collaborator.name ?? undefined,
          assignedUserRole: persona === "manager" ? "MANAGER" : "COLLABORATOR",
        })
      } else {
        await client.mutation(api.machines.updatePersona, {
          machineId: registration.machineId as Id<"machines">,
          persona: "",
        })
      }
    } else {
      await client.mutation(api.machines.updatePersona, {
        machineId: registration.machineId as Id<"machines">,
        persona: "",
      })
    }

    return jsonWithCors(
      {
        machineId: registration.machineId,
        tenantId: registration.tenantId,
        companyId: registration.companyId,
        companySlug: registration.companySlug,
        machineToken: registration.machineToken,
        machineEmail: account.authEmail,
        expiresAt: registration.expiresAt,
        persona: persona ?? null,
        assignedUserId: assignedUserId ?? null,
        collaborator: collaborator ?? null,
      },
      { status: 201 },
      origin,
      CORS_METHODS
    )
  } catch (error) {
    console.error("[machines.register] Falha no provisionamento", error)
    const details = error instanceof Error ? error.message : String(error)
    const msg = details.toLowerCase()
    const isInvalidCode = msg.includes("código de provisionamento inválido")
    const isCompanyNotFound = msg.includes("empresa não encontrada")
    const isConvexError = msg.includes("convexerror")
    const status = isInvalidCode ? 401 : isCompanyNotFound ? 404 : isConvexError ? 400 : 500
    const payload = { error: "Falha ao provisionar máquina", details }
    return jsonWithCors(payload, status, origin, CORS_METHODS)
  }
}
