import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { assertPermission, actorLite, AppError, requireActor, can } from "./lib/access";
import { audit } from "./lib/audit";
import { notify } from "./lib/notify";
import { getSettings } from "./lib/settings";
import { fmtMoney } from "../src/lib/format";

const TRANSITIONS: Record<string, string[]> = {
  PENDING: ["APPROVED", "CANCELLED"],
  APPROVED: ["PAID", "CANCELLED", "PENDING"],
  PAID: ["REVERSED"],
  CANCELLED: ["PENDING"],
  REVERSED: [],
};

export const updateStatus = mutation({
  args: { ids: v.array(v.id("commissions")), status: v.string(), note: v.optional(v.string()) },
  returns: v.object({ updated: v.number() }),
  handler: async (ctx, { ids, status, note }) => {
    const user = await assertPermission(ctx, "commissions.manage");
    const settings = await getSettings(ctx);
    if (!["APPROVED", "PAID", "CANCELLED", "REVERSED", "PENDING"].includes(status)) throw new AppError("Invalid status", "VALIDATION");
    if ((status === "CANCELLED" || status === "REVERSED") && !note) throw new AppError("A note is required to cancel or reverse a commission.", "VALIDATION");
    let updated = 0;
    const byWorker = new Map<string, number>();
    const now = Date.now();
    for (const id of ids) {
      const c = await ctx.db.get(id);
      if (!c || !TRANSITIONS[c.status]?.includes(status)) continue;
      const [worker, r] = await Promise.all([ctx.db.get(c.workerId), ctx.db.get(c.reservationId)]);
      await ctx.db.patch(id, { status, adminNotes: note ?? c.adminNotes, approvedAt: status === "APPROVED" ? now : c.approvedAt, approvedById: status === "APPROVED" ? user.id : c.approvedById, paidAt: status === "PAID" ? now : status === "REVERSED" ? c.paidAt : undefined, history: [...c.history, { from: c.status, to: status, by: user.fullName, at: now, note }] });
      await audit(ctx, actorLite(user), { action: "COMMISSION_CHANGED", module: "commissions", entityType: "commission", entityId: id, entityLabel: `${c.code} · ${worker?.fullName ?? ""}`, previousValue: c.status, newValue: status, reason: note, reservationId: c.reservationId });
      void r;
      updated++;
      if (status === "APPROVED" || status === "PAID") byWorker.set(c.workerId, (byWorker.get(c.workerId) ?? 0) + c.amount);
    }
    for (const [workerId, amount] of byWorker) await notify(ctx, { type: status === "PAID" ? "COMMISSION_PAID" : "COMMISSION_APPROVED", title: status === "PAID" ? "Commission paid" : "Commission approved", body: `${fmtMoney(amount, settings.currency)} of commission ${status === "PAID" ? "has been paid out" : "was approved"} by ${user.fullName}.`, href: "/my-commission", targetUserIds: [workerId as never], actorId: user.id });
    return { updated };
  },
});

/** All commissions (managers) or the caller's own (workers). */
export const list = query({
  args: { workerId: v.optional(v.id("users")), status: v.optional(v.string()) },
  returns: v.array(v.any()),
  handler: async (ctx, { workerId, status }) => {
    const actor = await requireActor(ctx);
    const all = can(actor, "commissions.view_all") || can(actor, "commissions.manage");
    if (!all && !can(actor, "commissions.view_own")) throw new AppError("You don't have permission to do this.", "PERMISSION");
    const target = all ? workerId : actor.id;
    const rows = target ? await ctx.db.query("commissions").withIndex("by_worker", (q) => q.eq("workerId", target)).order("desc").take(1000) : await ctx.db.query("commissions").order("desc").take(1000);
    return Promise.all(
      rows
        .filter((c) => !status || c.status === status)
        .map(async (c) => {
          const [w, r, ap] = await Promise.all([ctx.db.get(c.workerId), ctx.db.get(c.reservationId), c.approvedById ? ctx.db.get(c.approvedById) : null]);
          const [cu, a] = r ? await Promise.all([ctx.db.get(r.customerId), ctx.db.get(r.apartmentId)]) : [null, null];
          return { id: c._id, code: c.code, amount: c.amount, status: c.status, triggerEvent: c.triggerEvent, createdAt: c.createdAt, approvedAt: c.approvedAt ?? null, paidAt: c.paidAt ?? null, adminNotes: c.adminNotes ?? null, history: c.history, worker: { id: c.workerId, fullName: w?.fullName ?? "" }, reservation: r ? { id: r._id, code: r.code, checkIn: r.checkIn, status: r.status, customer: cu?.fullName ?? "", apartment: a?.code ?? "" } : null, approvedBy: ap?.fullName ?? null };
        })
    );
  },
});
