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

import { requireAdmin, requireUser } from "./rbac"

type AnyCtx = MutationCtx | QueryCtx

function normalizeSlug(input: string) {
  return input
    .trim()
    .toLowerCase()
    .normalize("NFD")
    .replace(/[^\w\s-]/g, "")
    .replace(/\s+/g, "-")
    .replace(/-+/g, "-")
}

async function ensureUniqueSlug(ctx: AnyCtx, tenantId: string, slug: string, excludeId?: Id<"deviceExportTemplates">) {
  const existing = await ctx.db
    .query("deviceExportTemplates")
    .withIndex("by_tenant_slug", (q) => q.eq("tenantId", tenantId).eq("slug", slug))
    .first()

  if (existing && (!excludeId || existing._id !== excludeId)) {
    throw new ConvexError("Já existe um template com este identificador")
  }
}

async function unsetDefaults(
  ctx: MutationCtx,
  tenantId: string,
  companyId: Id<"companies"> | undefined | null,
  excludeId?: Id<"deviceExportTemplates">
) {
  const templates = await ctx.db
    .query("deviceExportTemplates")
    .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
    .take(100)

  await Promise.all(
    templates
      .filter((tpl) => tpl._id !== excludeId)
      .filter((tpl) => {
        if (companyId) {
          return tpl.companyId === companyId
        }
        return !tpl.companyId
      })
      .map((tpl) => ctx.db.patch(tpl._id, { isDefault: false }))
  )
}

function normalizeColumns(columns: { key: string; label?: string | null }[]) {
  return columns
    .map((col) => ({
      key: col.key.trim(),
      label: col.label?.trim() || undefined,
    }))
    .filter((col) => col.key.length > 0)
}

export const list = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    companyId: v.optional(v.id("companies")),
    includeInactive: v.optional(v.boolean()),
  },
  handler: async (ctx, { tenantId, viewerId, companyId, includeInactive }) => {
    await requireAdmin(ctx, viewerId, tenantId)
    const templates = await ctx.db
      .query("deviceExportTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(100)

    return templates
      .filter((tpl) => {
        if (!includeInactive && tpl.isActive === false) return false
        if (!companyId) return true
        if (!tpl.companyId) return true
        return tpl.companyId === companyId
      })
      .sort((a, b) => a.name.localeCompare(b.name, "pt-BR"))
      .map((tpl) => ({
        id: tpl._id,
        slug: tpl.slug,
        name: tpl.name,
        description: tpl.description ?? "",
        columns: tpl.columns ?? [],
        filters: tpl.filters ?? null,
        companyId: tpl.companyId ?? null,
        isDefault: Boolean(tpl.isDefault),
        isActive: tpl.isActive ?? true,
        createdAt: tpl.createdAt,
        updatedAt: tpl.updatedAt,
        createdBy: tpl.createdBy ?? null,
        updatedBy: tpl.updatedBy ?? null,
      }))
  },
})

export const listForTenant = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    companyId: v.optional(v.id("companies")),
  },
  handler: async (ctx, { tenantId, viewerId, companyId }) => {
    await requireUser(ctx, viewerId, tenantId)
    const templates = await ctx.db
      .query("deviceExportTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(100)

    return templates
      .filter((tpl) => tpl.isActive !== false)
      .filter((tpl) => {
        if (!companyId) return !tpl.companyId
        return !tpl.companyId || tpl.companyId === companyId
      })
      .sort((a, b) => a.name.localeCompare(b.name, "pt-BR"))
      .map((tpl) => ({
        id: tpl._id,
        slug: tpl.slug,
        name: tpl.name,
        description: tpl.description ?? "",
        columns: tpl.columns ?? [],
        filters: tpl.filters ?? null,
        companyId: tpl.companyId ?? null,
        isDefault: Boolean(tpl.isDefault),
        isActive: tpl.isActive ?? true,
      }))
  },
})

export const getDefault = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
    companyId: v.optional(v.id("companies")),
  },
  handler: async (ctx, { tenantId, viewerId, companyId }) => {
    await requireUser(ctx, viewerId, tenantId)
    const indexQuery = companyId
      ? ctx.db
          .query("deviceExportTemplates")
          .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId).eq("companyId", companyId))
      : ctx.db.query("deviceExportTemplates").withIndex("by_tenant_default", (q) => q.eq("tenantId", tenantId).eq("isDefault", true))

    const templates = await indexQuery.take(100)
    const candidate = templates.find((tpl) => tpl.isDefault) ?? null
    if (candidate) {
      return {
        id: candidate._id,
        slug: candidate.slug,
        name: candidate.name,
        description: candidate.description ?? "",
        columns: candidate.columns ?? [],
        filters: candidate.filters ?? null,
        companyId: candidate.companyId ?? null,
        isDefault: Boolean(candidate.isDefault),
        isActive: candidate.isActive ?? true,
      }
    }

    if (companyId) {
      const globalDefault = await ctx.db
        .query("deviceExportTemplates")
        .withIndex("by_tenant_default", (q) => q.eq("tenantId", tenantId).eq("isDefault", true))
        .first()

      if (globalDefault) {
        return {
          id: globalDefault._id,
          slug: globalDefault.slug,
          name: globalDefault.name,
          description: globalDefault.description ?? "",
          columns: globalDefault.columns ?? [],
          filters: globalDefault.filters ?? null,
          companyId: globalDefault.companyId ?? null,
          isDefault: Boolean(globalDefault.isDefault),
          isActive: globalDefault.isActive ?? true,
        }
      }
    }

    return null
  },
})

export const create = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    name: v.string(),
    description: v.optional(v.string()),
    columns: v.array(
      v.object({
        key: v.string(),
        label: v.optional(v.string()),
      })
    ),
    filters: v.optional(v.any()),
    companyId: v.optional(v.id("companies")),
    isDefault: v.optional(v.boolean()),
    isActive: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    await requireAdmin(ctx, args.actorId, args.tenantId)
    const normalizedName = args.name.trim()
    if (normalizedName.length < 3) {
      throw new ConvexError("Informe um nome para o template")
    }
    const slug = normalizeSlug(normalizedName)
    if (!slug) {
      throw new ConvexError("Não foi possível gerar um identificador para o template")
    }
    await ensureUniqueSlug(ctx, args.tenantId, slug)

    const columns = normalizeColumns(args.columns)
    if (columns.length === 0) {
      throw new ConvexError("Selecione ao menos uma coluna")
    }

    const now = Date.now()
    const templateId = await ctx.db.insert("deviceExportTemplates", {
      tenantId: args.tenantId,
      name: normalizedName,
      slug,
      description: args.description ?? undefined,
      columns,
      filters: args.filters ?? undefined,
      companyId: args.companyId ?? undefined,
      isDefault: Boolean(args.isDefault),
      isActive: args.isActive ?? true,
      createdBy: args.actorId,
      updatedBy: args.actorId,
      createdAt: now,
      updatedAt: now,
    })

    if (args.isDefault) {
      await unsetDefaults(ctx, args.tenantId, args.companyId ?? null, templateId)
    }

    return templateId
  },
})

export const update = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("deviceExportTemplates"),
    name: v.string(),
    description: v.optional(v.string()),
    columns: v.array(
      v.object({
        key: v.string(),
        label: v.optional(v.string()),
      })
    ),
    filters: v.optional(v.any()),
    companyId: v.optional(v.id("companies")),
    isDefault: v.optional(v.boolean()),
    isActive: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    await requireAdmin(ctx, args.actorId, args.tenantId)
    const template = await ctx.db.get(args.templateId)
    if (!template || template.tenantId !== args.tenantId) {
      throw new ConvexError("Template não encontrado")
    }
    const normalizedName = args.name.trim()
    if (normalizedName.length < 3) {
      throw new ConvexError("Informe um nome para o template")
    }
    let slug = template.slug
    if (template.name !== normalizedName) {
      slug = normalizeSlug(normalizedName)
      if (!slug) throw new ConvexError("Não foi possível gerar um identificador para o template")
      await ensureUniqueSlug(ctx, args.tenantId, slug, args.templateId)
    }

    const columns = normalizeColumns(args.columns)
    if (columns.length === 0) {
      throw new ConvexError("Selecione ao menos uma coluna")
    }

    const isDefault = Boolean(args.isDefault)
    await ctx.db.patch(args.templateId, {
      name: normalizedName,
      slug,
      description: args.description ?? undefined,
      columns,
      filters: args.filters ?? undefined,
      companyId: args.companyId ?? undefined,
      isDefault,
      isActive: args.isActive ?? true,
      updatedAt: Date.now(),
      updatedBy: args.actorId,
    })

    if (isDefault) {
      await unsetDefaults(ctx, args.tenantId, args.companyId ?? null, args.templateId)
    }
  },
})

export const remove = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("deviceExportTemplates"),
  },
  handler: async (ctx, args) => {
    await requireAdmin(ctx, args.actorId, args.tenantId)
    const template = await ctx.db.get(args.templateId)
    if (!template || template.tenantId !== args.tenantId) {
      throw new ConvexError("Template não encontrado")
    }
    await ctx.db.delete(args.templateId)
  },
})

export const setDefault = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("deviceExportTemplates"),
  },
  handler: async (ctx, args) => {
    await requireAdmin(ctx, args.actorId, args.tenantId)
    const template = await ctx.db.get(args.templateId)
    if (!template || template.tenantId !== args.tenantId) {
      throw new ConvexError("Template não encontrado")
    }
    await unsetDefaults(ctx, args.tenantId, template.companyId ?? null, args.templateId)
    await ctx.db.patch(args.templateId, {
      isDefault: true,
      updatedAt: Date.now(),
      updatedBy: args.actorId,
    })
  },
})

export const clearCompanyDefault = 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 templates = await ctx.db
      .query("deviceExportTemplates")
      .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId).eq("companyId", companyId))
      .take(100)
    const now = Date.now()
    await Promise.all(
      templates.map((tpl) =>
        ctx.db.patch(tpl._id, {
          isDefault: false,
          updatedAt: now,
          updatedBy: actorId,
        })
      )
    )
  },
})
