import { NextResponse } from "next/server"
import { z } from "zod"
import { hashPassword } from "better-auth/crypto"
import { ConvexHttpClient } from "convex/browser"

import { prisma } from "@/lib/prisma"
import { requireAuthenticatedSession } from "@/lib/auth-server"
import { env } from "@/lib/env"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { api } from "@/convex/_generated/api"
import { ensureCollaboratorAccount } from "@/server/machines-auth"

const updateSchema = z.object({
  email: z.string().email().optional(),
  password: z
    .object({
      newPassword: z.string().min(8, "A nova senha deve ter pelo menos 8 caracteres."),
      confirmPassword: z.string().min(8),
    })
    .optional(),
})

export async function PATCH(request: Request) {
  const session = await requireAuthenticatedSession()
  const normalizedRole = (session.user.role ?? "").toLowerCase()
  const persona = (session.user.machinePersona ?? "").toLowerCase()
  const allowedRoles = new Set(["collaborator", "manager", "admin", "agent"])
  const isMachinePersonaAllowed = normalizedRole === "machine" && (persona === "collaborator" || persona === "manager")
  if (!allowedRoles.has(normalizedRole) && !isMachinePersonaAllowed) {
    return NextResponse.json({ error: "Acesso não autorizado" }, { status: 403 })
  }
  const effectiveRole =
    normalizedRole === "admin"
      ? "ADMIN"
      : normalizedRole === "agent"
        ? "AGENT"
        : normalizedRole === "manager"
          ? "MANAGER"
          : normalizedRole === "collaborator"
            ? "COLLABORATOR"
            : persona === "manager"
              ? "MANAGER"
              : "COLLABORATOR"

  let payload: unknown
  try {
    payload = await request.json()
  } catch {
    return NextResponse.json({ error: "Payload inválido" }, { status: 400 })
  }

  const parsed = updateSchema.safeParse(payload)
  if (!parsed.success) {
    return NextResponse.json({ error: "Dados inválidos", details: parsed.error.flatten() }, { status: 400 })
  }

  const { email: emailInput, password } = parsed.data
  const currentEmail = session.user.email.trim().toLowerCase()
  const authUserId = session.user.id
  const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID

  let newEmail = emailInput?.trim().toLowerCase()
  if (newEmail && newEmail === currentEmail) {
    newEmail = undefined
  }

  if (password && password.newPassword !== password.confirmPassword) {
    return NextResponse.json({ error: "As senhas informadas não conferem." }, { status: 400 })
  }

  if (newEmail) {
    const existingEmail = await prisma.authUser.findUnique({ where: { email: newEmail } })
    if (existingEmail && existingEmail.id !== authUserId) {
      return NextResponse.json({ error: "Já existe um usuário com este e-mail." }, { status: 409 })
    }
  }

  const domainUser = await prisma.user.findUnique({ where: { email: currentEmail } })
  const companyId = domainUser?.companyId ?? null
  const name = session.user.name ?? currentEmail

  await prisma.$transaction(async (tx) => {
    if (newEmail) {
      await tx.authUser.update({
        where: { id: authUserId },
        data: { email: newEmail },
      })

      const existingAccount = await tx.authAccount.findUnique({
        where: {
          providerId_accountId: {
            providerId: "credential",
            accountId: currentEmail,
          },
        },
      })

      if (existingAccount) {
        await tx.authAccount.update({
          where: { id: existingAccount.id },
          data: { accountId: newEmail },
        })
      } else {
        await tx.authAccount.create({
          data: {
            providerId: "credential",
            accountId: newEmail,
            userId: authUserId,
            password: null,
          },
        })
      }

      await tx.user.updateMany({
        where: { email: currentEmail },
        data: { email: newEmail },
      })
    }

    if (password) {
      const hashed = await hashPassword(password.newPassword)
      await tx.authAccount.upsert({
        where: {
          providerId_accountId: {
            providerId: "credential",
            accountId: newEmail ?? currentEmail,
          },
        },
        update: {
          password: hashed,
          userId: authUserId,
        },
        create: {
          providerId: "credential",
          accountId: newEmail ?? currentEmail,
          userId: authUserId,
          password: hashed,
        },
      })
    }
  })

  const effectiveEmail = newEmail ?? currentEmail

  await prisma.user.upsert({
    where: { email: effectiveEmail },
    update: {
      name,
      tenantId,
      role: effectiveRole,
      companyId: companyId ?? undefined,
    },
    create: {
      email: effectiveEmail,
      name,
      tenantId,
      role: effectiveRole,
      companyId: companyId ?? undefined,
    },
  })

  await ensureCollaboratorAccount({
    email: effectiveEmail,
    name,
    tenantId,
    companyId,
    role: effectiveRole,
  })

  if (env.NEXT_PUBLIC_CONVEX_URL) {
    try {
      const client = new ConvexHttpClient(env.NEXT_PUBLIC_CONVEX_URL)
      await client.mutation(api.users.ensureUser, {
        tenantId,
        email: effectiveEmail,
        name,
        role: effectiveRole,
      })
    } catch (error) {
      console.warn("[portal.profile] Falha ao sincronizar usuário no Convex", error)
    }
  }

  return NextResponse.json({ ok: true, email: effectiveEmail })
}
