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();
}

function normalizeMode(value?: string): "business" | "calendar" {
  if (value === "business") return "business";
  return "calendar";
}

function normalizeThreshold(value?: number): number {
  if (value === undefined || value === null) return 0.8;
  if (value < 0.1) return 0.1;
  if (value > 0.95) return 0.95;
  return value;
}

const VALID_PAUSE_STATUSES = ["PAUSED", "PENDING", "AWAITING_ATTENDANCE"] as const;

function normalizePauseStatuses(statuses?: string[]): string[] {
  if (!statuses || statuses.length === 0) return ["PAUSED"];
  const filtered = statuses.filter((s) => VALID_PAUSE_STATUSES.includes(s as typeof VALID_PAUSE_STATUSES[number]));
  return filtered.length > 0 ? filtered : ["PAUSED"];
}

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,
      responseMode: policy.responseMode ?? "calendar",
      timeToResolution: policy.timeToResolution ?? null,
      solutionMode: policy.solutionMode ?? "calendar",
      alertThreshold: policy.alertThreshold ?? 0.8,
      pauseStatuses: policy.pauseStatuses ?? ["PAUSED"],
    }));
  },
});

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()),
    responseMode: v.optional(v.string()),
    timeToResolution: v.optional(v.number()),
    solutionMode: v.optional(v.string()),
    alertThreshold: v.optional(v.number()),
    pauseStatuses: v.optional(v.array(v.string())),
  },
  handler: async (ctx, args) => {
    const { tenantId, actorId, name, description, timeToFirstResponse, responseMode, timeToResolution, solutionMode, alertThreshold, pauseStatuses } = args;
    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,
      responseMode: normalizeMode(responseMode),
      timeToResolution,
      solutionMode: normalizeMode(solutionMode),
      alertThreshold: normalizeThreshold(alertThreshold),
      pauseStatuses: normalizePauseStatuses(pauseStatuses),
    });
    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()),
    responseMode: v.optional(v.string()),
    timeToResolution: v.optional(v.number()),
    solutionMode: v.optional(v.string()),
    alertThreshold: v.optional(v.number()),
    pauseStatuses: v.optional(v.array(v.string())),
  },
  handler: async (ctx, args) => {
    const { policyId, tenantId, actorId, name, description, timeToFirstResponse, responseMode, timeToResolution, solutionMode, alertThreshold, pauseStatuses } = args;
    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,
      responseMode: normalizeMode(responseMode),
      timeToResolution,
      solutionMode: normalizeMode(solutionMode),
      alertThreshold: normalizeThreshold(alertThreshold),
      pauseStatuses: normalizePauseStatuses(pauseStatuses),
    });
  },
});

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);
  },
});
