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

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 CategorySlaRuleInput = {
  priority: string
  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(),
  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)
}

export const get = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    categoryId: v.id("ticketCategories"),
  },
  handler: async (ctx, { tenantId, viewerId, categoryId }) => {
    await requireAdmin(ctx, viewerId, tenantId)
    const category = await ctx.db.get(categoryId)
    if (!category || category.tenantId !== tenantId) {
      throw new ConvexError("Categoria não encontrada")
    }
    const records = await ctx.db
      .query("categorySlaSettings")
      .withIndex("by_tenant_category", (q) => q.eq("tenantId", tenantId).eq("categoryId", categoryId))
      .take(100)

    return {
      categoryId,
      categoryName: category.name,
      rules: records.map((record) => ({
        priority: record.priority,
        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"],
      })),
    }
  },
})

export const save = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    categoryId: v.id("ticketCategories"),
    rules: v.array(ruleInput),
  },
  handler: async (ctx, { tenantId, actorId, categoryId, rules }) => {
    await requireAdmin(ctx, actorId, tenantId)
    const category = await ctx.db.get(categoryId)
    if (!category || category.tenantId !== tenantId) {
      throw new ConvexError("Categoria não encontrada")
    }
    const sanitized = sanitizeRules(rules)
    const existing = await ctx.db
      .query("categorySlaSettings")
      .withIndex("by_tenant_category", (q) => q.eq("tenantId", tenantId).eq("categoryId", categoryId))
      .take(100)
    await Promise.all(existing.map((record) => ctx.db.delete(record._id)))

    const now = Date.now()
    for (const rule of sanitized) {
      await ctx.db.insert("categorySlaSettings", {
        tenantId,
        categoryId,
        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,
      })
    }
  },
})

function sanitizeRules(rules: CategorySlaRuleInput[]) {
  const normalized: Record<string, ReturnType<typeof buildRule>> = {}
  for (const rule of rules) {
    const built = buildRule(rule)
    normalized[built.priority] = built
  }
  return Object.values(normalized)
}

function buildRule(rule: CategorySlaRuleInput) {
  const priority = normalizePriority(rule.priority)
  const responseTargetMinutes = sanitizeTime(rule.responseTargetMinutes)
  const solutionTargetMinutes = sanitizeTime(rule.solutionTargetMinutes)
  return {
    priority,
    responseTargetMinutes,
    responseMode: normalizeMode(rule.responseMode),
    solutionTargetMinutes,
    solutionMode: normalizeMode(rule.solutionMode),
    alertThreshold: normalizeThreshold(rule.alertThreshold),
    pauseStatuses: normalizePauseStatuses(rule.pauseStatuses),
    calendarType: rule.calendarType ?? null,
  }
}
