import { ConvexError, v } from "convex/values"

import type { Doc, Id } from "./_generated/dataModel"
import { mutation, query } from "./_generated/server"
import { requireAdmin, requireStaff } from "./rbac"
import { normalizeChecklistText } from "./ticketChecklist"

function normalizeTemplateName(input: string) {
  return input.trim()
}

function normalizeTemplateDescription(input: string | null | undefined) {
  const text = (input ?? "").trim()
  return text.length > 0 ? text : null
}

type ChecklistItemType = "checkbox" | "question"

type RawTemplateItem = {
  id?: string
  text: string
  description?: string
  type?: string
  options?: string[]
  required?: boolean
}

type NormalizedTemplateItem = {
  id: string
  text: string
  description?: string
  type?: ChecklistItemType
  options?: string[]
  required?: boolean
}

function normalizeTemplateItems(
  raw: RawTemplateItem[],
  options: { generateId?: () => string }
): NormalizedTemplateItem[] {
  if (!Array.isArray(raw) || raw.length === 0) {
    throw new ConvexError("Adicione pelo menos um item no checklist.")
  }

  const generateId = options.generateId ?? (() => crypto.randomUUID())
  const seen = new Set<string>()
  const items: NormalizedTemplateItem[] = []

  for (const entry of raw) {
    const id = String(entry.id ?? "").trim() || generateId()
    if (seen.has(id)) {
      throw new ConvexError("Itens do checklist com IDs duplicados.")
    }
    seen.add(id)

    const text = normalizeChecklistText(entry.text)
    if (!text) {
      throw new ConvexError("Todos os itens do checklist precisam ter um texto.")
    }
    if (text.length > 240) {
      throw new ConvexError("Item do checklist muito longo (máx. 240 caracteres).")
    }

    const description = entry.description?.trim() || undefined
    const itemType: ChecklistItemType = entry.type === "question" ? "question" : "checkbox"
    const itemOptions = itemType === "question" && Array.isArray(entry.options)
      ? entry.options.map((o) => String(o).trim()).filter((o) => o.length > 0)
      : undefined

    if (itemType === "question" && (!itemOptions || itemOptions.length < 2)) {
      throw new ConvexError(`A pergunta "${text}" precisa ter pelo menos 2 opções.`)
    }

    const required = typeof entry.required === "boolean" ? entry.required : true
    items.push({
      id,
      text,
      description,
      type: itemType,
      options: itemOptions,
      required,
    })
  }

  return items
}

function mapTemplate(template: Doc<"ticketChecklistTemplates">, company: Doc<"companies"> | null) {
  return {
    id: template._id,
    name: template.name,
    description: template.description ?? "",
    company: company ? { id: company._id, name: company.name } : null,
    items: (template.items ?? []).map((item) => ({
      id: item.id,
      text: item.text,
      description: item.description,
      type: item.type ?? "checkbox",
      options: item.options,
      required: typeof item.required === "boolean" ? item.required : true,
    })),
    isArchived: Boolean(template.isArchived),
    createdAt: template.createdAt,
    updatedAt: template.updatedAt,
  }
}

export const listActive = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    companyId: v.optional(v.id("companies")),
  },
  handler: async (ctx, { tenantId, viewerId, companyId }) => {
    await requireStaff(ctx, viewerId, tenantId)

    const templates = await ctx.db
      .query("ticketChecklistTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(200)

    const filtered = templates.filter((tpl) => {
      if (tpl.isArchived === true) return false
      if (!companyId) return true
      return !tpl.companyId || String(tpl.companyId) === String(companyId)
    })

    const companiesToHydrate = new Map<string, Id<"companies">>()
    for (const tpl of filtered) {
      if (tpl.companyId) {
        companiesToHydrate.set(String(tpl.companyId), tpl.companyId)
      }
    }

    const companyMap = new Map<string, Doc<"companies">>()
    for (const id of companiesToHydrate.values()) {
      const company = await ctx.db.get(id)
      if (company && company.tenantId === tenantId) {
        companyMap.set(String(id), company as Doc<"companies">)
      }
    }

    return filtered
      .sort((a, b) => {
        const aSpecific = a.companyId ? 1 : 0
        const bSpecific = b.companyId ? 1 : 0
        if (aSpecific !== bSpecific) return bSpecific - aSpecific
        return (a.name ?? "").localeCompare(b.name ?? "", "pt-BR")
      })
      .map((tpl) => mapTemplate(tpl, tpl.companyId ? (companyMap.get(String(tpl.companyId)) ?? null) : null))
  },
})

export const list = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    includeArchived: v.optional(v.boolean()),
  },
  handler: async (ctx, { tenantId, viewerId, includeArchived }) => {
    await requireAdmin(ctx, viewerId, tenantId)

    const templates = await ctx.db
      .query("ticketChecklistTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(500)

    const filtered = templates.filter((tpl) => includeArchived || tpl.isArchived !== true)

    const companiesToHydrate = new Map<string, Id<"companies">>()
    for (const tpl of filtered) {
      if (tpl.companyId) {
        companiesToHydrate.set(String(tpl.companyId), tpl.companyId)
      }
    }

    const companyMap = new Map<string, Doc<"companies">>()
    for (const id of companiesToHydrate.values()) {
      const company = await ctx.db.get(id)
      if (company && company.tenantId === tenantId) {
        companyMap.set(String(id), company as Doc<"companies">)
      }
    }

    return filtered
      .sort((a, b) => {
        const aSpecific = a.companyId ? 1 : 0
        const bSpecific = b.companyId ? 1 : 0
        if (aSpecific !== bSpecific) return bSpecific - aSpecific
        return (a.name ?? "").localeCompare(b.name ?? "", "pt-BR")
      })
      .map((tpl) => mapTemplate(tpl, tpl.companyId ? (companyMap.get(String(tpl.companyId)) ?? null) : null))
  },
})

export const create = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    name: v.string(),
    description: v.optional(v.string()),
    companyId: v.optional(v.id("companies")),
    items: v.array(
      v.object({
        id: v.optional(v.string()),
        text: v.string(),
        description: v.optional(v.string()),
        type: v.optional(v.string()),
        options: v.optional(v.array(v.string())),
        required: v.optional(v.boolean()),
      }),
    ),
    isArchived: v.optional(v.boolean()),
  },
  handler: async (ctx, { tenantId, actorId, name, description, companyId, items, isArchived }) => {
    await requireAdmin(ctx, actorId, tenantId)

    const normalizedName = normalizeTemplateName(name)
    if (normalizedName.length < 3) {
      throw new ConvexError("Informe um nome com pelo menos 3 caracteres.")
    }

    if (companyId) {
      const company = await ctx.db.get(companyId)
      if (!company || company.tenantId !== tenantId) {
        throw new ConvexError("Empresa inválida para o template.")
      }
    }

    const normalizedItems = normalizeTemplateItems(items, {})
    const normalizedDescription = normalizeTemplateDescription(description)
    const archivedFlag = typeof isArchived === "boolean" ? isArchived : false
    const now = Date.now()

    return ctx.db.insert("ticketChecklistTemplates", {
      tenantId,
      name: normalizedName,
      description: normalizedDescription ?? undefined,
      companyId: companyId ?? undefined,
      items: normalizedItems,
      isArchived: archivedFlag,
      createdAt: now,
      updatedAt: now,
      createdBy: actorId,
      updatedBy: actorId,
    })
  },
})

export const update = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("ticketChecklistTemplates"),
    name: v.string(),
    description: v.optional(v.string()),
    companyId: v.optional(v.id("companies")),
    items: v.array(
      v.object({
        id: v.optional(v.string()),
        text: v.string(),
        description: v.optional(v.string()),
        type: v.optional(v.string()),
        options: v.optional(v.array(v.string())),
        required: v.optional(v.boolean()),
      }),
    ),
    isArchived: v.optional(v.boolean()),
  },
  handler: async (ctx, { tenantId, actorId, templateId, name, description, companyId, items, isArchived }) => {
    await requireAdmin(ctx, actorId, tenantId)

    const existing = await ctx.db.get(templateId)
    if (!existing || existing.tenantId !== tenantId) {
      throw new ConvexError("Template de checklist não encontrado.")
    }

    const normalizedName = normalizeTemplateName(name)
    if (normalizedName.length < 3) {
      throw new ConvexError("Informe um nome com pelo menos 3 caracteres.")
    }

    if (companyId) {
      const company = await ctx.db.get(companyId)
      if (!company || company.tenantId !== tenantId) {
        throw new ConvexError("Empresa inválida para o template.")
      }
    }

    const normalizedItems = normalizeTemplateItems(items, {})
    const normalizedDescription = normalizeTemplateDescription(description)
    const nextArchived = typeof isArchived === "boolean" ? isArchived : Boolean(existing.isArchived)
    const now = Date.now()

    await ctx.db.patch(templateId, {
      name: normalizedName,
      description: normalizedDescription ?? undefined,
      companyId: companyId ?? undefined,
      items: normalizedItems,
      isArchived: nextArchived,
      updatedAt: now,
      updatedBy: actorId,
    })

    return { ok: true }
  },
})

export const remove = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("ticketChecklistTemplates"),
  },
  handler: async (ctx, { tenantId, actorId, templateId }) => {
    await requireAdmin(ctx, actorId, tenantId)

    const existing = await ctx.db.get(templateId)
    if (!existing || existing.tenantId !== tenantId) {
      throw new ConvexError("Template de checklist não encontrado.")
    }

    await ctx.db.delete(templateId)

    return { ok: true }
  },
})

// DEBUG: Query para verificar dados do template e checklist de um ticket
export const debugTemplateAndTicketChecklist = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    templateId: v.id("ticketChecklistTemplates"),
    ticketId: v.optional(v.id("tickets")),
  },
  handler: async (ctx, { tenantId, viewerId, templateId, ticketId }) => {
    await requireStaff(ctx, viewerId, tenantId)

    const template = await ctx.db.get(templateId)
    if (!template || template.tenantId !== tenantId) {
      return { error: "Template nao encontrado" }
    }

    const templateData = {
      id: String(template._id),
      name: template.name,
      description: template.description,
      hasDescription: Boolean(template.description),
      descriptionType: typeof template.description,
      itemsCount: template.items?.length ?? 0,
    }

    let ticketData = null
    if (ticketId) {
      const ticket = await ctx.db.get(ticketId)
      if (ticket && ticket.tenantId === tenantId) {
        ticketData = {
          id: String(ticket._id),
          checklistCount: ticket.checklist?.length ?? 0,
          checklistItems: (ticket.checklist ?? []).map((item) => ({
            id: item.id,
            text: item.text.substring(0, 50),
            templateId: item.templateId ? String(item.templateId) : null,
            templateDescription: item.templateDescription,
            hasTemplateDescription: Boolean(item.templateDescription),
            description: item.description,
            hasDescription: Boolean(item.description),
          })),
        }
      }
    }

    return { template: templateData, ticket: ticketData }
  },
})
