import { mutation, query } from "./_generated/server"
import { ConvexError, v } from "convex/values"
import type { Id } from "./_generated/dataModel"

import { requireAdmin } from "./rbac"

const PRIORITY_VALUES = ["URGENT", "HIGH", "MEDIUM", "LOW", "DEFAULT"] as const
const VALID_STATUSES = ["PENDING", "AWAITING_ATTENDANCE", "PAUSED", "RESOLVED"] as const
const VALID_TIME_MODES = ["business", "calendar"] as const

type CompanySlaRuleInput = {
  priority: string
  categoryId?: string | null
  responseTargetMinutes?: number | null
  responseMode?: string | null
  solutionTargetMinutes?: number | null
  solutionMode?: string | null
  alertThreshold?: number | null
  pauseStatuses?: string[] | null
  calendarType?: string | null
}

const ruleInput = v.object({
  priority: v.string(),
  categoryId: v.optional(v.union(v.id("ticketCategories"), v.null())),
  responseTargetMinutes: v.optional(v.number()),
  responseMode: v.optional(v.string()),
  solutionTargetMinutes: v.optional(v.number()),
  solutionMode: v.optional(v.string()),
  alertThreshold: v.optional(v.number()),
  pauseStatuses: v.optional(v.array(v.string())),
  calendarType: v.optional(v.string()),
})

function normalizePriority(value: string) {
  const upper = value.trim().toUpperCase()
  return PRIORITY_VALUES.includes(upper as (typeof PRIORITY_VALUES)[number]) ? upper : "DEFAULT"
}

function sanitizeTime(value?: number | null) {
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return undefined
  return Math.round(value)
}

function normalizeMode(value?: string | null) {
  if (!value) return "calendar"
  const normalized = value.toLowerCase()
  return VALID_TIME_MODES.includes(normalized as (typeof VALID_TIME_MODES)[number]) ? normalized : "calendar"
}

function normalizeThreshold(value?: number | null) {
  if (typeof value !== "number" || Number.isNaN(value)) {
    return 0.8
  }
  const clamped = Math.min(Math.max(value, 0.1), 0.95)
  return Math.round(clamped * 100) / 100
}

function normalizePauseStatuses(value?: string[] | null) {
  if (!Array.isArray(value)) return ["PAUSED"]
  const normalized = new Set<string>()
  for (const status of value) {
    if (typeof status !== "string") continue
    const upper = status.trim().toUpperCase()
    if (VALID_STATUSES.includes(upper as (typeof VALID_STATUSES)[number])) {
      normalized.add(upper)
    }
  }
  if (normalized.size === 0) {
    normalized.add("PAUSED")
  }
  return Array.from(normalized)
}

// Lista todas as empresas que possuem SLA customizado
export const listCompaniesWithCustomSla = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
  },
  handler: async (ctx, { tenantId, viewerId }) => {
    await requireAdmin(ctx, viewerId, tenantId)

    // Busca todas as configurações de SLA por empresa
    const allSettings = await ctx.db
      .query("companySlaSettings")
      .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId))
      .take(1000)

    // Agrupa por companyId para evitar duplicatas
    const companyIds = [...new Set(allSettings.map((s) => s.companyId))]

    // Busca dados das empresas
    const companies = await Promise.all(
      companyIds.map(async (companyId) => {
        const company = await ctx.db.get(companyId)
        if (!company) return null
        const rulesCount = allSettings.filter((s) => s.companyId === companyId).length
        return {
          companyId,
          companyName: company.name,
          companySlug: company.slug,
          rulesCount,
        }
      })
    )

    return companies.filter(Boolean)
  },
})

// Busca as regras de SLA de uma empresa específica
export const get = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    companyId: v.id("companies"),
  },
  handler: async (ctx, { tenantId, viewerId, companyId }) => {
    await requireAdmin(ctx, viewerId, tenantId)

    const company = await ctx.db.get(companyId)
    if (!company || company.tenantId !== tenantId) {
      throw new ConvexError("Empresa não encontrada")
    }

    const records = await ctx.db
      .query("companySlaSettings")
      .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId).eq("companyId", companyId))
      .take(100)

    // Busca nomes das categorias referenciadas
    const categoryIds = [...new Set(records.filter((r) => r.categoryId).map((r) => r.categoryId!))]
    const categories = await Promise.all(categoryIds.map((id) => ctx.db.get(id)))
    const categoryNames = new Map(
      categories.filter(Boolean).map((c) => [c!._id, c!.name])
    )

    return {
      companyId,
      companyName: company.name,
      rules: records.map((record) => ({
        priority: record.priority,
        categoryId: record.categoryId ?? null,
        categoryName: record.categoryId ? categoryNames.get(record.categoryId) ?? null : null,
        responseTargetMinutes: record.responseTargetMinutes ?? null,
        responseMode: record.responseMode ?? "calendar",
        solutionTargetMinutes: record.solutionTargetMinutes ?? null,
        solutionMode: record.solutionMode ?? "calendar",
        alertThreshold: record.alertThreshold ?? 0.8,
        pauseStatuses: record.pauseStatuses ?? ["PAUSED"],
      })),
    }
  },
})

// Salva as regras de SLA de uma empresa
export const save = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    companyId: v.id("companies"),
    rules: v.array(ruleInput),
  },
  handler: async (ctx, { tenantId, actorId, companyId, rules }) => {
    await requireAdmin(ctx, actorId, tenantId)

    const company = await ctx.db.get(companyId)
    if (!company || company.tenantId !== tenantId) {
      throw new ConvexError("Empresa não encontrada")
    }

    // Valida categorias referenciadas
    for (const rule of rules) {
      if (rule.categoryId) {
        const category = await ctx.db.get(rule.categoryId)
        if (!category || category.tenantId !== tenantId) {
          throw new ConvexError(`Categoria inválida: ${rule.categoryId}`)
        }
      }
    }

    const sanitized = sanitizeRules(rules)

    // Remove regras existentes da empresa
    const existing = await ctx.db
      .query("companySlaSettings")
      .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId).eq("companyId", companyId))
      .take(100)

    await Promise.all(existing.map((record) => ctx.db.delete(record._id)))

    // Insere novas regras
    const now = Date.now()
    for (const rule of sanitized) {
      await ctx.db.insert("companySlaSettings", {
        tenantId,
        companyId,
        categoryId: rule.categoryId ? (rule.categoryId as Id<"ticketCategories">) : undefined,
        priority: rule.priority,
        responseTargetMinutes: rule.responseTargetMinutes,
        responseMode: rule.responseMode,
        solutionTargetMinutes: rule.solutionTargetMinutes,
        solutionMode: rule.solutionMode,
        alertThreshold: rule.alertThreshold,
        pauseStatuses: rule.pauseStatuses,
        calendarType: rule.calendarType ?? undefined,
        createdAt: now,
        updatedAt: now,
        actorId,
      })
    }

    return { ok: true }
  },
})

// Remove todas as regras de SLA de uma empresa
export const remove = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    companyId: v.id("companies"),
  },
  handler: async (ctx, { tenantId, actorId, companyId }) => {
    await requireAdmin(ctx, actorId, tenantId)

    const company = await ctx.db.get(companyId)
    if (!company || company.tenantId !== tenantId) {
      throw new ConvexError("Empresa não encontrada")
    }

    const existing = await ctx.db
      .query("companySlaSettings")
      .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId).eq("companyId", companyId))
      .take(100)

    await Promise.all(existing.map((record) => ctx.db.delete(record._id)))

    return { ok: true }
  },
})

function sanitizeRules(rules: CompanySlaRuleInput[]) {
  // Chave única: categoryId + priority
  const normalized: Map<string, ReturnType<typeof buildRule>> = new Map()

  for (const rule of rules) {
    const built = buildRule(rule)
    const key = `${built.categoryId ?? "ALL"}-${built.priority}`
    normalized.set(key, built)
  }

  return Array.from(normalized.values())
}

function buildRule(rule: CompanySlaRuleInput) {
  const priority = normalizePriority(rule.priority)
  const responseTargetMinutes = sanitizeTime(rule.responseTargetMinutes)
  const solutionTargetMinutes = sanitizeTime(rule.solutionTargetMinutes)

  return {
    priority,
    categoryId: rule.categoryId ?? null,
    responseTargetMinutes,
    responseMode: normalizeMode(rule.responseMode),
    solutionTargetMinutes,
    solutionMode: normalizeMode(rule.solutionMode),
    alertThreshold: normalizeThreshold(rule.alertThreshold),
    pauseStatuses: normalizePauseStatuses(rule.pauseStatuses),
    calendarType: rule.calendarType ?? null,
  }
}
