import "dotenv/config"
import { ConvexHttpClient } from "convex/browser"

import { createPrismaClient } from "./utils/prisma-client.mjs"

const prisma = createPrismaClient()

const tenantId = process.env.SYNC_TENANT_ID || "tenant-atlas"
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL || "http://127.0.0.1:3210"
const secret = process.env.CONVEX_SYNC_SECRET
const STAFF_ROSTER = [
  { email: "admin@sistema.dev", name: "Administrador", role: "ADMIN" },
  { email: "gabriel.oliveira@rever.com.br", name: "Gabriel Oliveira", role: "AGENT" },
  { email: "george.araujo@rever.com.br", name: "George Araujo", role: "AGENT" },
  { email: "hugo.soares@rever.com.br", name: "Hugo Soares", role: "AGENT" },
  { email: "julio@rever.com.br", name: "Julio Cesar", role: "AGENT" },
  { email: "lorena@rever.com.br", name: "Lorena Magalhães", role: "AGENT" },
  { email: "renan.pac@paulicon.com.br", name: "Rever", role: "AGENT" },
  { email: "suporte@rever.com.br", name: "Telão", role: "AGENT" },
  { email: "thiago.medeiros@rever.com.br", name: "Thiago Medeiros", role: "AGENT" },
  { email: "weslei@rever.com.br", name: "Weslei Magalhães", role: "AGENT" },
]

const rawDefaultAssigneeEmail = process.env.SYNC_DEFAULT_ASSIGNEE || "gabriel.oliveira@rever.com.br"

if (!secret) {
  console.error("CONVEX_SYNC_SECRET não configurado. Configure no .env.")
  process.exit(1)
}

const allowedRoles = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"]) 

const client = new ConvexHttpClient(convexUrl)

function normalizeEmail(email) {
  if (!email) return null
  return email.trim().toLowerCase()
}

const defaultAssigneeEmail = normalizeEmail(rawDefaultAssigneeEmail)

function toDate(value) {
  if (!value && value !== 0) return null
  return new Date(value)
}

function slugify(value) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^\w\s-]/g, "")
    .trim()
    .replace(/\s+/g, "-")
    .replace(/-+/g, "-")
    .toLowerCase()
}

const STATUS_MAP = {
  NEW: "PENDING",
  PENDING: "PENDING",
  OPEN: "AWAITING_ATTENDANCE",
  AWAITING_ATTENDANCE: "AWAITING_ATTENDANCE",
  ON_HOLD: "PAUSED",
  PAUSED: "PAUSED",
  RESOLVED: "RESOLVED",
  CLOSED: "RESOLVED",
}

function normalizeStatus(status) {
  if (!status) return "PENDING"
  const key = String(status).toUpperCase()
  return STATUS_MAP[key] ?? "PENDING"
}

function serializeConvexSlaSnapshot(snapshot) {
  if (!snapshot || typeof snapshot !== "object") return null
  const categoryIdRaw = snapshot.categoryId
  let categoryId
  if (typeof categoryIdRaw === "string") {
    categoryId = categoryIdRaw
  } else if (categoryIdRaw && typeof categoryIdRaw === "object" && "_id" in categoryIdRaw) {
    categoryId = categoryIdRaw._id
  }

  const pauseStatuses =
    Array.isArray(snapshot.pauseStatuses) && snapshot.pauseStatuses.length > 0
      ? snapshot.pauseStatuses.filter((value) => typeof value === "string")
      : undefined

  const normalized = {
    categoryId,
    categoryName: typeof snapshot.categoryName === "string" ? snapshot.categoryName : undefined,
    priority: typeof snapshot.priority === "string" ? snapshot.priority : undefined,
    responseTargetMinutes: typeof snapshot.responseTargetMinutes === "number" ? snapshot.responseTargetMinutes : undefined,
    responseMode: typeof snapshot.responseMode === "string" ? snapshot.responseMode : undefined,
    solutionTargetMinutes: typeof snapshot.solutionTargetMinutes === "number" ? snapshot.solutionTargetMinutes : undefined,
    solutionMode: typeof snapshot.solutionMode === "string" ? snapshot.solutionMode : undefined,
    alertThreshold: typeof snapshot.alertThreshold === "number" ? snapshot.alertThreshold : undefined,
    pauseStatuses,
  }

  const hasValues = Object.values(normalized).some((value) => value !== undefined)
  return hasValues ? normalized : null
}

async function upsertCompanies(snapshotCompanies) {
  const map = new Map()

  for (const company of snapshotCompanies) {
    const slug = company.slug || slugify(company.name)
    const record = await prisma.company.upsert({
      where: {
        tenantId_slug: {
          tenantId,
          slug,
        },
      },
      update: {
        name: company.name,
        isAvulso: Boolean(company.isAvulso ?? false),
        cnpj: company.cnpj ?? null,
        domain: company.domain ?? null,
        phone: company.phone ?? null,
        description: company.description ?? null,
        address: company.address ?? null,
      },
      create: {
        tenantId,
        name: company.name,
        slug,
        isAvulso: Boolean(company.isAvulso ?? false),
        cnpj: company.cnpj ?? null,
        domain: company.domain ?? null,
        phone: company.phone ?? null,
        description: company.description ?? null,
        address: company.address ?? null,
        createdAt: toDate(company.createdAt) ?? new Date(),
        updatedAt: toDate(company.updatedAt) ?? new Date(),
      },
    })

    map.set(slug, record.id)
  }

  return map
}

async function upsertUsers(snapshotUsers, companyMap) {
  const map = new Map()

  for (const user of snapshotUsers) {
    const normalizedEmail = normalizeEmail(user.email)
    if (!normalizedEmail) continue

    const normalizedRole = (user.role ?? "MANAGER").toUpperCase()
    const role = allowedRoles.has(normalizedRole) ? normalizedRole : "MANAGER"
    const companyId = user.companySlug ? companyMap.get(user.companySlug) ?? null : null

    const record = await prisma.user.upsert({
      where: { email: normalizedEmail },
      update: {
        name: user.name ?? normalizedEmail,
        role,
        tenantId,
        avatarUrl: user.avatarUrl ?? null,
        companyId,
      },
      create: {
        email: normalizedEmail,
        name: user.name ?? normalizedEmail,
        role,
        tenantId,
        avatarUrl: user.avatarUrl ?? null,
        companyId,
      },
    })

    map.set(normalizedEmail, record.id)
  }

  for (const staff of STAFF_ROSTER) {
    const normalizedEmail = normalizeEmail(staff.email)
    if (!normalizedEmail) continue
    const record = await prisma.user.upsert({
      where: { email: normalizedEmail },
      update: {
        name: staff.name,
        role: staff.role,
        tenantId,
        companyId: null,
      },
      create: {
        email: normalizedEmail,
        name: staff.name,
        role: staff.role,
        tenantId,
        avatarUrl: null,
        companyId: null,
      },
    })
    map.set(normalizedEmail, record.id)
  }

  const allowedStaffEmails = new Set(STAFF_ROSTER.map((staff) => normalizeEmail(staff.email)).filter(Boolean))

  const removableStaff = await prisma.user.findMany({
    where: {
      tenantId,
      role: { in: ["ADMIN", "AGENT", "COLLABORATOR"] },
      email: {
        notIn: Array.from(allowedStaffEmails),
      },
    },
  })

  const fallbackAssigneeId = defaultAssigneeEmail ? map.get(defaultAssigneeEmail) ?? null : null

  for (const staff of removableStaff) {
    if (fallbackAssigneeId) {
      await prisma.ticket.updateMany({
        where: { tenantId, assigneeId: staff.id },
        data: { assigneeId: fallbackAssigneeId },
      })
      await prisma.ticketComment.updateMany({
        where: { authorId: staff.id },
        data: { authorId: fallbackAssigneeId },
      })
    }

    await prisma.user.update({
      where: { id: staff.id },
      data: {
      role: "MANAGER",
      },
    })
  }

  return map
}

async function upsertQueues(snapshotQueues) {
  const map = new Map()

  for (const queue of snapshotQueues) {
    if (!queue.slug) continue
    const record = await prisma.queue.upsert({
      where: {
        tenantId_slug: {
          tenantId,
          slug: queue.slug,
        },
      },
      update: {
        name: queue.name,
      },
      create: {
        tenantId,
        name: queue.name,
        slug: queue.slug,
      },
    })

    map.set(queue.slug, record.id)
  }

  return map
}

async function upsertTickets(snapshotTickets, userMap, queueMap, companyMap) {
  let created = 0
  let updated = 0

  const fallbackAssigneeId = defaultAssigneeEmail ? userMap.get(defaultAssigneeEmail) ?? null : null

  for (const ticket of snapshotTickets) {
    if (!ticket.requesterEmail) continue

    const requesterId = userMap.get(normalizeEmail(ticket.requesterEmail))
    if (!requesterId) continue

    const queueId = ticket.queueSlug ? queueMap.get(ticket.queueSlug) ?? null : null

    let companyId = ticket.companySlug ? companyMap.get(ticket.companySlug) ?? null : null
    if (!companyId && requesterId) {
      const requester = await prisma.user.findUnique({
        where: { id: requesterId },
        select: { companyId: true },
      })
      companyId = requester?.companyId ?? null
    }

    const desiredAssigneeEmail = defaultAssigneeEmail || normalizeEmail(ticket.assigneeEmail)
    const assigneeId = desiredAssigneeEmail ? userMap.get(desiredAssigneeEmail) || fallbackAssigneeId || null : fallbackAssigneeId || null

    const slaSnapshot = serializeConvexSlaSnapshot(ticket.slaSnapshot)

    const existing = await prisma.ticket.findFirst({
      where: {
        tenantId,
        reference: ticket.reference,
      },
    })

    const data = {
      subject: ticket.subject,
      summary: ticket.summary ?? null,
      status: normalizeStatus(ticket.status),
      priority: (ticket.priority ?? "MEDIUM").toUpperCase(),
      channel: (ticket.channel ?? "MANUAL").toUpperCase(),
      queueId,
      requesterId,
      assigneeId,
      dueAt: toDate(ticket.dueAt),
      firstResponseAt: toDate(ticket.firstResponseAt),
      resolvedAt: toDate(ticket.resolvedAt),
      closedAt: toDate(ticket.closedAt),
      createdAt: toDate(ticket.createdAt) ?? new Date(),
      updatedAt: toDate(ticket.updatedAt) ?? new Date(),
      companyId,
      slaSnapshot: slaSnapshot ?? null,
      slaResponseDueAt: toDate(ticket.slaResponseDueAt),
      slaSolutionDueAt: toDate(ticket.slaSolutionDueAt),
      slaResponseStatus: typeof ticket.slaResponseStatus === "string" ? ticket.slaResponseStatus : null,
      slaSolutionStatus: typeof ticket.slaSolutionStatus === "string" ? ticket.slaSolutionStatus : null,
      slaPausedAt: toDate(ticket.slaPausedAt),
      slaPausedBy: typeof ticket.slaPausedBy === "string" ? ticket.slaPausedBy : null,
      slaPausedMs: typeof ticket.slaPausedMs === "number" ? ticket.slaPausedMs : null,
    }

    let ticketRecord

    if (existing) {
      ticketRecord = await prisma.ticket.update({
        where: { id: existing.id },
        data,
      })
      updated += 1
    } else {
      ticketRecord = await prisma.ticket.create({
        data: {
          tenantId,
          reference: ticket.reference,
          ...data,
        },
      })
      created += 1
    }

    await prisma.ticketComment.deleteMany({ where: { ticketId: ticketRecord.id } })
    await prisma.ticketEvent.deleteMany({ where: { ticketId: ticketRecord.id } })

    const commentsData = ticket.comments
      .map((comment) => {
        const authorId = comment.authorEmail ? userMap.get(normalizeEmail(comment.authorEmail)) : null
        if (!authorId) {
          return null
        }
        return {
          ticketId: ticketRecord.id,
          authorId,
          visibility: (comment.visibility ?? "INTERNAL").toUpperCase(),
          body: comment.body ?? "",
          attachments: null,
          createdAt: toDate(comment.createdAt) ?? new Date(),
          updatedAt: toDate(comment.updatedAt) ?? new Date(),
        }
      })
      .filter(Boolean)

    if (commentsData.length > 0) {
      await prisma.ticketComment.createMany({ data: commentsData })
    }

    const eventsData = ticket.events.map((event) => ({
      ticketId: ticketRecord.id,
      type: event.type ?? "UNKNOWN",
      payload: event.payload ?? {},
      createdAt: toDate(event.createdAt) ?? new Date(),
    }))

    if (eventsData.length > 0) {
      await prisma.ticketEvent.createMany({ data: eventsData })
    }
  }

  return { created, updated }
}

async function run() {
  console.log("Baixando snapshot do Convex...")
  const snapshot = await client.query("migrations:exportTenantSnapshot", {
    secret,
    tenantId,
  })

  console.log(`Empresas recebidas: ${snapshot.companies.length}`)
  console.log(`Usuários recebidos: ${snapshot.users.length}`)
  console.log(`Filas recebidas: ${snapshot.queues.length}`)
  console.log(`Tickets recebidos: ${snapshot.tickets.length}`)

  console.log("Sincronizando empresas no Prisma...")
  const companyMap = await upsertCompanies(snapshot.companies)
  console.log(`Empresas ativas no mapa: ${companyMap.size}`)

  console.log("Sincronizando usuários no Prisma...")
  const userMap = await upsertUsers(snapshot.users, companyMap)
  console.log(`Usuários ativos no mapa: ${userMap.size}`)

  console.log("Sincronizando filas no Prisma...")
  const queueMap = await upsertQueues(snapshot.queues)
  console.log(`Filas ativas no mapa: ${queueMap.size}`)

  console.log("Sincronizando tickets no Prisma...")
  const ticketStats = await upsertTickets(snapshot.tickets, userMap, queueMap, companyMap)
  console.log(`Tickets criados: ${ticketStats.created}`)
  console.log(`Tickets atualizados: ${ticketStats.updated}`)
}

run()
  .catch((error) => {
    console.error("Falha ao importar dados do Convex para Prisma", error)
    process.exitCode = 1
  })
  .finally(async () => {
    await prisma.$disconnect()
  })
