import { NextResponse } from "next/server"

import { Prisma } from "@prisma/client"
import { hashPassword } from "better-auth/crypto"
import { ConvexHttpClient } from "convex/browser"

import { api } from "@/convex/_generated/api"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { env } from "@/lib/env"
import { prisma } from "@/lib/prisma"
import {
  computeInviteStatus,
  normalizeInvite,
  normalizeRoleOption,
  type NormalizedInvite,
} from "@/server/invite-utils"

type AcceptInvitePayload = {
  name?: string
  password: string
}

function validatePassword(password: string) {
  return password.length >= 8
}

async function syncInvite(invite: NormalizedInvite) {
  const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
  if (!convexUrl) return
  const client = new ConvexHttpClient(convexUrl)
  await client.mutation(api.invites.sync, {
    tenantId: invite.tenantId,
    inviteId: invite.id,
    email: invite.email,
    name: invite.name ?? undefined,
    role: invite.role.toUpperCase(),
    status: invite.status,
    token: invite.token,
    expiresAt: Date.parse(invite.expiresAt),
    createdAt: Date.parse(invite.createdAt),
    createdById: invite.createdById ?? undefined,
    acceptedAt: invite.acceptedAt ? Date.parse(invite.acceptedAt) : undefined,
    acceptedById: invite.acceptedById ?? undefined,
    revokedAt: invite.revokedAt ? Date.parse(invite.revokedAt) : undefined,
    revokedById: invite.revokedById ?? undefined,
    revokedReason: invite.revokedReason ?? undefined,
  })
}

export async function GET(_request: Request, context: { params: Promise<{ token: string }> }) {
  const { token } = await context.params
  const invite = await prisma.authInvite.findUnique({
    where: { token },
    include: { events: { orderBy: { createdAt: "asc" } } },
  })

  if (!invite) {
    return NextResponse.json({ error: "Convite não encontrado" }, { status: 404 })
  }

  const now = new Date()
  const status = computeInviteStatus(invite, now)

  if (status !== invite.status) {
    await prisma.authInvite.update({ where: { id: invite.id }, data: { status } })
    const event = await prisma.authInviteEvent.create({
      data: {
        inviteId: invite.id,
        type: status,
        payload: Prisma.JsonNull,
        actorId: null,
      },
    })
    invite.status = status
    invite.events.push(event)
  }

  const normalized = normalizeInvite(invite, now)
  await syncInvite(normalized)

  return NextResponse.json({ invite: normalized })
}

export async function POST(request: Request, context: { params: Promise<{ token: string }> }) {
  const { token } = await context.params
  const payload = (await request.json().catch(() => null)) as Partial<AcceptInvitePayload> | null
  if (!payload || typeof payload.password !== "string") {
    return NextResponse.json({ error: "Senha inválida" }, { status: 400 })
  }

  if (!validatePassword(payload.password)) {
    return NextResponse.json({ error: "Senha deve conter pelo menos 8 caracteres" }, { status: 400 })
  }

  const invite = await prisma.authInvite.findUnique({
    where: { token },
    include: { events: { orderBy: { createdAt: "asc" } } },
  })

  if (!invite) {
    return NextResponse.json({ error: "Convite não encontrado" }, { status: 404 })
  }

  const now = new Date()
  const status = computeInviteStatus(invite, now)

  if (status === "expired") {
    await prisma.authInvite.update({ where: { id: invite.id }, data: { status: "expired" } })
    const event = await prisma.authInviteEvent.create({
      data: {
        inviteId: invite.id,
        type: "expired",
        payload: Prisma.JsonNull,
        actorId: null,
      },
    })
    invite.status = "expired"
    invite.events.push(event)
    const normalizedExpired = normalizeInvite(invite, now)
    await syncInvite(normalizedExpired)
    return NextResponse.json({ error: "Convite expirado" }, { status: 410 })
  }

  if (status === "revoked") {
    return NextResponse.json({ error: "Convite revogado" }, { status: 410 })
  }

  if (status === "accepted") {
    return NextResponse.json({ error: "Convite já utilizado" }, { status: 409 })
  }

  const existingUser = await prisma.authUser.findUnique({ where: { email: invite.email } })
  if (existingUser) {
    return NextResponse.json({ error: "Usuário já registrado" }, { status: 409 })
  }

  const name = typeof payload.name === "string" && payload.name.trim() ? payload.name.trim() : invite.name || invite.email
  const tenantId = invite.tenantId || DEFAULT_TENANT_ID
  const role = normalizeRoleOption(invite.role)

  const hashedPassword = await hashPassword(payload.password)

  const user = await prisma.authUser.create({
    data: {
      email: invite.email,
      name,
      role,
      tenantId,
      accounts: {
        create: {
          providerId: "credential",
          accountId: invite.email,
          password: hashedPassword,
        },
      },
    },
  })

  const updatedInvite = await prisma.authInvite.update({
    where: { id: invite.id },
    data: {
      status: "accepted",
      acceptedAt: now,
      acceptedById: user.id,
      name,
    },
  })

  const event = await prisma.authInviteEvent.create({
    data: {
      inviteId: invite.id,
      type: "accepted",
      payload: { userId: user.id },
      actorId: user.id,
    },
  })

  const normalized = normalizeInvite({ ...updatedInvite, events: [...invite.events, event] }, now)
  await syncInvite(normalized)

  const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
  if (convexUrl) {
    try {
      const convex = new ConvexHttpClient(convexUrl)
      await convex.mutation(api.users.ensureUser, {
        tenantId,
        email: invite.email,
        name,
        avatarUrl: undefined,
        role: role.toUpperCase(),
      })
    } catch (error) {
      console.warn("Falha ao sincronizar usuário no Convex", error)
    }
  }

  return NextResponse.json({ success: true })
}
