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 } from "./rbac";

function normalizeName(value: string) {
  return value.trim();
}

type AnyCtx = QueryCtx | MutationCtx;

async function ensureUniqueName(ctx: AnyCtx, tenantId: string, name: string, excludeId?: Id<"slaPolicies">) {
  const existing = await ctx.db
    .query("slaPolicies")
    .withIndex("by_tenant_name", (q) => q.eq("tenantId", tenantId).eq("name", name))
    .first();
  if (existing && (!excludeId || existing._id !== excludeId)) {
    throw new ConvexError("Já existe uma política SLA com este nome");
  }
}

export const list = query({
  args: { tenantId: v.string(), viewerId: v.id("users") },
  handler: async (ctx, { tenantId, viewerId }) => {
    await requireAdmin(ctx, viewerId, tenantId);
    const items = await ctx.db
      .query("slaPolicies")
      .withIndex("by_tenant_name", (q) => q.eq("tenantId", tenantId))
      .take(50);

    return items.map((policy) => ({
      id: policy._id,
      name: policy.name,
      description: policy.description ?? "",
      timeToFirstResponse: policy.timeToFirstResponse ?? null,
      timeToResolution: policy.timeToResolution ?? null,
    }));
  },
});

export const create = mutation({
  args: {
    tenantId: v.string(),
    actorId: v.id("users"),
    name: v.string(),
    description: v.optional(v.string()),
    timeToFirstResponse: v.optional(v.number()),
    timeToResolution: v.optional(v.number()),
  },
  handler: async (ctx, { tenantId, actorId, name, description, timeToFirstResponse, timeToResolution }) => {
    await requireAdmin(ctx, actorId, tenantId);
    const trimmed = normalizeName(name);
    if (trimmed.length < 2) {
      throw new ConvexError("Informe um nome válido para a política");
    }
    await ensureUniqueName(ctx, tenantId, trimmed);
    if (timeToFirstResponse !== undefined && timeToFirstResponse < 0) {
      throw new ConvexError("Tempo para primeira resposta deve ser positivo");
    }
    if (timeToResolution !== undefined && timeToResolution < 0) {
      throw new ConvexError("Tempo para resolução deve ser positivo");
    }

    const id = await ctx.db.insert("slaPolicies", {
      tenantId,
      name: trimmed,
      description,
      timeToFirstResponse,
      timeToResolution,
    });
    return id;
  },
});

export const update = mutation({
  args: {
    policyId: v.id("slaPolicies"),
    tenantId: v.string(),
    actorId: v.id("users"),
    name: v.string(),
    description: v.optional(v.string()),
    timeToFirstResponse: v.optional(v.number()),
    timeToResolution: v.optional(v.number()),
  },
  handler: async (ctx, { policyId, tenantId, actorId, name, description, timeToFirstResponse, timeToResolution }) => {
    await requireAdmin(ctx, actorId, tenantId);
    const policy = await ctx.db.get(policyId);
    if (!policy || policy.tenantId !== tenantId) {
      throw new ConvexError("Política não encontrada");
    }
    const trimmed = normalizeName(name);
    if (trimmed.length < 2) {
      throw new ConvexError("Informe um nome válido para a política");
    }
    if (timeToFirstResponse !== undefined && timeToFirstResponse < 0) {
      throw new ConvexError("Tempo para primeira resposta deve ser positivo");
    }
    if (timeToResolution !== undefined && timeToResolution < 0) {
      throw new ConvexError("Tempo para resolução deve ser positivo");
    }
    await ensureUniqueName(ctx, tenantId, trimmed, policyId);

    await ctx.db.patch(policyId, {
      name: trimmed,
      description,
      timeToFirstResponse,
      timeToResolution,
    });
  },
});

export const remove = mutation({
  args: {
    policyId: v.id("slaPolicies"),
    tenantId: v.string(),
    actorId: v.id("users"),
  },
  handler: async (ctx, { policyId, tenantId, actorId }) => {
    await requireAdmin(ctx, actorId, tenantId);
    const policy = await ctx.db.get(policyId);
    if (!policy || policy.tenantId !== tenantId) {
      throw new ConvexError("Política não encontrada");
    }

    const ticketLinked = await ctx.db
      .query("tickets")
      .withIndex("by_tenant_sla_policy", (q) => q.eq("tenantId", tenantId).eq("slaPolicyId", policyId))
      .first();
    if (ticketLinked) {
      throw new ConvexError("Remova a associação de tickets antes de excluir a política");
    }

    await ctx.db.delete(policyId);
  },
});
