"use server";

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

import { requireAdmin, requireStaff } from "./rbac";
import { TICKET_FORM_CONFIG } from "./ticketForms.config";

type AnyCtx = MutationCtx | QueryCtx;

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

export function normalizeFormTemplateKey(input: string | null | undefined): string | null {
  if (!input) return null;
  const normalized = slugify(input);
  return normalized || null;
}

async function templateKeyExists(ctx: AnyCtx, tenantId: string, key: string) {
  const existing = await ctx.db
    .query("ticketFormTemplates")
    .withIndex("by_tenant_key", (q) => q.eq("tenantId", tenantId).eq("key", key))
    .first();
  return Boolean(existing);
}

export async function ensureTicketFormTemplatesForTenant(ctx: MutationCtx, tenantId: string) {
  const existing = await ctx.db
    .query("ticketFormTemplates")
    .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
    .take(50);
  let order = existing.reduce((max, tpl) => Math.max(max, tpl.order ?? 0), 0);
  const now = Date.now();
  for (const template of TICKET_FORM_CONFIG) {
    const match = existing.find((tpl) => tpl.key === template.key);
    if (match) {
      const updates: Partial<Doc<"ticketFormTemplates">> = {};
      if (!match.baseTemplateKey) {
        updates.baseTemplateKey = template.key;
      }
      if (match.isSystem !== true) {
        updates.isSystem = true;
      }
      if (typeof match.defaultEnabled === "undefined") {
        updates.defaultEnabled = template.defaultEnabled;
      }
      if (Object.keys(updates).length) {
        await ctx.db.patch(match._id, {
          ...updates,
          updatedAt: now,
        });
      }
      continue;
    }
    order += 1;
    await ctx.db.insert("ticketFormTemplates", {
      tenantId,
      key: template.key,
      label: template.label,
      description: template.description ?? undefined,
      defaultEnabled: template.defaultEnabled,
      baseTemplateKey: template.key,
      isSystem: true,
      isArchived: false,
      order,
      createdAt: now,
      updatedAt: now,
    });
  }
}

export async function getTemplateByKey(ctx: AnyCtx, tenantId: string, key: string): Promise<Doc<"ticketFormTemplates"> | null> {
  return ctx.db
    .query("ticketFormTemplates")
    .withIndex("by_tenant_key", (q) => q.eq("tenantId", tenantId).eq("key", key))
    .first();
}

async function generateTemplateKey(ctx: MutationCtx, tenantId: string, label: string) {
  const base = slugify(label) || `template-${Date.now()}`;
  let candidate = base;
  let suffix = 1;
  while (await templateKeyExists(ctx, tenantId, candidate)) {
    candidate = `${base}-${suffix}`;
    suffix += 1;
  }
  return candidate;
}

async function cloneFieldsFromTemplate(ctx: MutationCtx, tenantId: string, sourceKey: string, targetKey: string) {
  const sourceFields = await ctx.db
    .query("ticketFields")
    .withIndex("by_tenant_scope", (q) => q.eq("tenantId", tenantId).eq("scope", sourceKey))
    .take(50);
  if (sourceFields.length === 0) return;
  const ordered = await ctx.db
    .query("ticketFields")
    .withIndex("by_tenant_order", (q) => q.eq("tenantId", tenantId))
    .take(50);
  let order = ordered.reduce((max, field) => Math.max(max, field.order ?? 0), 0);
  const now = Date.now();
  for (const field of sourceFields.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))) {
    order += 1;
    await ctx.db.insert("ticketFields", {
      tenantId,
      key: field.key,
      label: field.label,
      description: field.description ?? undefined,
      type: field.type,
      required: field.required,
      options: field.options ?? undefined,
      scope: targetKey,
      companyId: field.companyId ?? undefined,
      order,
      createdAt: now,
      updatedAt: now,
    });
  }
}

function mapTemplate(template: Doc<"ticketFormTemplates">) {
  return {
    id: template._id,
    key: template.key,
    label: template.label,
    description: template.description ?? "",
    defaultEnabled: template.defaultEnabled ?? true,
    baseTemplateKey: template.baseTemplateKey ?? null,
    isSystem: Boolean(template.isSystem),
    isArchived: Boolean(template.isArchived),
    order: template.order ?? 0,
    createdAt: template.createdAt,
    updatedAt: template.updatedAt,
  };
}

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("ticketFormTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(50);
    return templates
      .filter((tpl) => includeArchived || tpl.isArchived !== true)
      .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.label.localeCompare(b.label, "pt-BR"))
      .map(mapTemplate);
  },
});

export const listActive = query({
  args: {
    tenantId: v.string(),
    viewerId: v.id("users"),
  },
  handler: async (ctx, { tenantId, viewerId }) => {
    await requireStaff(ctx, viewerId, tenantId);
    const templates = await ctx.db
      .query("ticketFormTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(50);
    return templates
      .filter((tpl) => tpl.isArchived !== true)
      .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.label.localeCompare(b.label, "pt-BR"))
      .map(mapTemplate);
  },
});

export const create = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    label: v.string(),
    description: v.optional(v.string()),
    baseTemplateKey: v.optional(v.string()),
    cloneFields: v.optional(v.boolean()),
  },
  handler: async (ctx, { tenantId, actorId, label, description, baseTemplateKey, cloneFields }) => {
    await requireAdmin(ctx, actorId, tenantId);
    const trimmedLabel = label.trim();
    if (trimmedLabel.length < 3) {
      throw new ConvexError("Informe um nome com pelo menos 3 caracteres");
    }
    const key = await generateTemplateKey(ctx, tenantId, trimmedLabel);
    const templates = await ctx.db
      .query("ticketFormTemplates")
      .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
      .take(50);
    const order = (templates.reduce((max, tpl) => Math.max(max, tpl.order ?? 0), 0) ?? 0) + 1;
    const now = Date.now();
    const templateId = await ctx.db.insert("ticketFormTemplates", {
      tenantId,
      key,
      label: trimmedLabel,
      description: description?.trim() || undefined,
      defaultEnabled: true,
      baseTemplateKey: baseTemplateKey ?? undefined,
      isSystem: false,
      isArchived: false,
      order,
      createdAt: now,
      updatedAt: now,
      createdBy: actorId,
      updatedBy: actorId,
    });
    if (baseTemplateKey && cloneFields) {
      await cloneFieldsFromTemplate(ctx, tenantId, baseTemplateKey, key);
    }
    return templateId;
  },
});

export const update = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("ticketFormTemplates"),
    label: v.string(),
    description: v.optional(v.string()),
    isArchived: v.optional(v.boolean()),
    defaultEnabled: v.optional(v.boolean()),
    order: v.optional(v.number()),
  },
  handler: async (ctx, { tenantId, actorId, templateId, label, description, isArchived, defaultEnabled, order }) => {
    await requireAdmin(ctx, actorId, tenantId);
    const template = await ctx.db.get(templateId);
    if (!template || template.tenantId !== tenantId) {
      throw new ConvexError("Template não encontrado");
    }
    const trimmedLabel = label.trim();
    if (trimmedLabel.length < 3) {
      throw new ConvexError("Informe um nome com pelo menos 3 caracteres");
    }
    await ctx.db.patch(templateId, {
      label: trimmedLabel,
      description: description?.trim() || undefined,
      isArchived: typeof isArchived === "boolean" ? isArchived : template.isArchived ?? false,
      defaultEnabled: typeof defaultEnabled === "boolean" ? defaultEnabled : template.defaultEnabled ?? true,
      order: typeof order === "number" ? order : template.order ?? 0,
      updatedAt: Date.now(),
      updatedBy: actorId,
    });
  },
});

export const archive = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    templateId: v.id("ticketFormTemplates"),
    archived: v.boolean(),
  },
  handler: async (ctx, { tenantId, actorId, templateId, archived }) => {
    await requireAdmin(ctx, actorId, tenantId);
    const template = await ctx.db.get(templateId);
    if (!template || template.tenantId !== tenantId) {
      throw new ConvexError("Template não encontrado");
    }
    await ctx.db.patch(templateId, {
      isArchived: archived,
      updatedAt: Date.now(),
      updatedBy: actorId,
    });
  },
});
