import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { assertPermission, can, actorLite, AppError, requireActor } from "./lib/access";
import { audit } from "./lib/audit";
import { notify } from "./lib/notify";
import { nextCode } from "./lib/seq";
import { getSettings } from "./lib/settings";
import { findConflicts, activeApartments } from "./lib/availability";
import { inventoryEvent, syncApartmentStatus } from "./lib/inventory";
import { todayKey, addDaysKey } from "./lib/days";
import { TASK_TYPES, TASK_STATUSES, PRIORITIES, MAINTENANCE_CATEGORIES, MAINTENANCE_STATUSES } from "../src/lib/domain";

const nullable = <T extends import("convex/values").Validator<unknown, "required", string>>(t: T) => v.optional(v.union(t, v.null()));

// ── Tasks ────────────────────────────────────────────────────
const taskArgs = {
  title: v.string(),
  description: nullable(v.string()),
  type: v.optional(v.string()),
  apartmentId: nullable(v.id("apartments")),
  reservationId: nullable(v.id("reservations")),
  customerId: nullable(v.id("customers")),
  assigneeId: nullable(v.id("users")),
  priority: v.optional(v.string()),
  dueDate: nullable(v.string()),
  dueTime: nullable(v.string()),
  notes: nullable(v.string()),
};

export const tasksList = query({
  args: { mineOnly: v.optional(v.boolean()) },
  returns: v.array(v.any()),
  handler: async (ctx, { mineOnly }) => {
    const actor = await assertPermission(ctx, "tasks.view");
    const cutoff14 = Date.now() - 14 * 86_400_000;
    const cutoff7 = Date.now() - 7 * 86_400_000;
    const all = await ctx.db.query("tasks").collect();
    const rows = all.filter((t) => (t.status === "TODO" || t.status === "IN_PROGRESS") || (t.completedAt && t.completedAt >= cutoff14) || (t.status === "CANCELLED" && t.updatedAt >= cutoff7)).filter((t) => !mineOnly || t.assigneeId === actor.id || t.createdById === actor.id);
    return Promise.all(
      rows.map(async (t) => {
        const [apartment, reservation, customer, assignee, createdBy] = await Promise.all([t.apartmentId ? ctx.db.get(t.apartmentId) : null, t.reservationId ? ctx.db.get(t.reservationId) : null, t.customerId ? ctx.db.get(t.customerId) : null, t.assigneeId ? ctx.db.get(t.assigneeId) : null, ctx.db.get(t.createdById)]);
        return { ...t, id: t._id, apartment: apartment ? { id: apartment._id, code: apartment.code } : null, reservation: reservation ? { id: reservation._id, code: reservation.code } : null, customer: customer ? { id: customer._id, fullName: customer.fullName } : null, assignee: assignee ? { id: assignee._id, fullName: assignee.fullName ?? "" } : null, createdBy: { fullName: createdBy?.fullName ?? "" } };
      })
    );
  },
});

export const createTask = mutation({
  args: taskArgs,
  returns: v.object({ id: v.id("tasks") }),
  handler: async (ctx, data) => {
    const user = await assertPermission(ctx, "tasks.create");
    if (data.title.trim().length < 2) throw new AppError("Title is required", "VALIDATION", { fields: { title: "Title is required" } });
    const type = data.type ?? "GENERAL";
    if (!TASK_TYPES.includes(type as (typeof TASK_TYPES)[number])) throw new AppError("Invalid type", "VALIDATION");
    const priority = data.priority ?? "MEDIUM";
    if (!PRIORITIES.includes(priority as (typeof PRIORITIES)[number])) throw new AppError("Invalid priority", "VALIDATION");
    if (data.assigneeId && data.assigneeId !== user.id && !can(user, "tasks.assign")) throw new AppError("You can only assign tasks to yourself.", "PERMISSION");
    const now = Date.now();
    const id = await ctx.db.insert("tasks", { title: data.title.trim(), description: data.description || undefined, type, apartmentId: data.apartmentId ?? undefined, reservationId: data.reservationId ?? undefined, customerId: data.customerId ?? undefined, assigneeId: data.assigneeId ?? undefined, createdById: user.id, priority, dueDate: data.dueDate || undefined, dueTime: data.dueTime || undefined, status: "TODO", notes: data.notes || undefined, createdAt: now, updatedAt: now });
    await audit(ctx, actorLite(user), { action: "TASK_CREATED", module: "tasks", entityType: "task", entityId: id, entityLabel: data.title, newValue: { type, priority, assignee: data.assigneeId ?? null }, apartmentId: data.apartmentId ?? null, reservationId: data.reservationId ?? null, customerId: data.customerId ?? null });
    if (data.assigneeId && data.assigneeId !== user.id) await notify(ctx, { type: "TASK_ASSIGNED", title: "New task assigned", body: `${user.fullName} assigned you: ${data.title}${data.dueDate ? ` (due ${data.dueDate}${data.dueTime ? " " + data.dueTime : ""})` : ""}.`, href: "/tasks", targetUserIds: [data.assigneeId], actorId: user.id, priority: priority === "URGENT" ? "HIGH" : "NORMAL" });
    return { id };
  },
});

export const updateTask = mutation({
  args: { id: v.id("tasks"), status: v.optional(v.string()), title: v.optional(v.string()), description: nullable(v.string()), type: v.optional(v.string()), apartmentId: nullable(v.id("apartments")), reservationId: nullable(v.id("reservations")), customerId: nullable(v.id("customers")), assigneeId: nullable(v.id("users")), priority: v.optional(v.string()), dueDate: nullable(v.string()), dueTime: nullable(v.string()), notes: nullable(v.string()) },
  returns: v.null(),
  handler: async (ctx, { id, status, ...data }) => {
    const user = await assertPermission(ctx, "tasks.edit");
    const t = await ctx.db.get(id);
    if (!t) throw new AppError("Task not found", "NOT_FOUND");
    if (!user.isAdmin && t.assigneeId !== user.id && t.createdById !== user.id && !can(user, "tasks.assign")) throw new AppError("You can only update your own tasks.", "PERMISSION");
    if (data.assigneeId && data.assigneeId !== t.assigneeId && data.assigneeId !== user.id && !can(user, "tasks.assign")) throw new AppError("You cannot reassign tasks.", "PERMISSION");
    if (status && !TASK_STATUSES.includes(status as (typeof TASK_STATUSES)[number])) throw new AppError("Invalid status", "VALIDATION");
    const now = Date.now();
    const patch: Record<string, unknown> = { updatedAt: now };
    for (const [k, val] of Object.entries(data)) if (val !== undefined) patch[k] = val === null || val === "" ? undefined : val;
    if (status) {
      patch.status = status;
      patch.completedAt = status === "COMPLETED" ? now : undefined;
    }
    await ctx.db.patch(id, patch);
    await audit(ctx, actorLite(user), { action: "TASK_UPDATED", module: "tasks", entityType: "task", entityId: id, entityLabel: t.title, previousValue: { status: t.status, assignee: t.assigneeId ?? null }, newValue: { status: status ?? t.status, ...(data.assigneeId !== undefined ? { assignee: data.assigneeId } : {}) }, apartmentId: t.apartmentId, reservationId: t.reservationId });
    if (data.assigneeId && data.assigneeId !== t.assigneeId && data.assigneeId !== user.id) await notify(ctx, { type: "TASK_ASSIGNED", title: "Task assigned to you", body: `${user.fullName} assigned you: ${t.title}.`, href: "/tasks", targetUserIds: [data.assigneeId], actorId: user.id });
    if (status === "COMPLETED" && t.type === "CLEANING" && t.apartmentId) {
      await ctx.db.patch(t.apartmentId, { cleaningStatus: "READY", updatedAt: now });
      for (const c of (await ctx.db.query("cleaningTasks").withIndex("by_apartment_status", (q) => q.eq("apartmentId", t.apartmentId!)).collect()).filter((c) => c.status === "NEEDS_CLEANING" || c.status === "IN_PROGRESS")) await ctx.db.patch(c._id, { status: "READY", completedAt: now, completedById: user.id });
      const settings = await getSettings(ctx);
      await syncApartmentStatus(ctx, t.apartmentId, settings.timezone);
    }
    return null;
  },
});

// ── Cleaning ─────────────────────────────────────────────────
export const cleaningList = query({
  args: {},
  returns: v.array(v.any()),
  handler: async (ctx) => {
    await assertPermission(ctx, "cleaning.view");
    const cutoff = Date.now() - 7 * 86_400_000;
    const rows = (await ctx.db.query("cleaningTasks").collect()).filter((c) => c.status !== "READY" || (c.completedAt ?? 0) >= cutoff).sort((a, b) => b.createdAt - a.createdAt);
    return Promise.all(
      rows.map(async (c) => {
        const [apartment, reservation, assignee, completedBy] = await Promise.all([ctx.db.get(c.apartmentId), c.reservationId ? ctx.db.get(c.reservationId) : null, c.assigneeId ? ctx.db.get(c.assigneeId) : null, c.completedById ? ctx.db.get(c.completedById) : null]);
        const r = reservation ? { id: reservation._id, code: reservation.code, customer: (await ctx.db.get(reservation.customerId))?.fullName ?? "" } : null;
        return { ...c, id: c._id, apartment: apartment ? { id: apartment._id, code: apartment.code, name: apartment.name, cleaningStatus: apartment.cleaningStatus, status: apartment.status } : null, reservation: r, assignee: assignee ? { id: assignee._id, fullName: assignee.fullName ?? "" } : null, completedBy: completedBy?.fullName ?? null };
      })
    );
  },
});

export const assignCleaning = mutation({
  args: { cleaningTaskId: v.id("cleaningTasks"), assigneeId: v.union(v.id("users"), v.null()) },
  returns: v.null(),
  handler: async (ctx, { cleaningTaskId, assigneeId }) => {
    const user = await assertPermission(ctx, "cleaning.manage");
    const c = await ctx.db.get(cleaningTaskId);
    if (!c) throw new AppError("Cleaning task not found", "NOT_FOUND");
    const apt = await ctx.db.get(c.apartmentId);
    await ctx.db.patch(cleaningTaskId, { assigneeId: assigneeId ?? undefined });
    for (const t of await ctx.db.query("tasks").withIndex("by_apartment", (q) => q.eq("apartmentId", c.apartmentId)).collect()) if (t.type === "CLEANING" && (t.status === "TODO" || t.status === "IN_PROGRESS")) await ctx.db.patch(t._id, { assigneeId: assigneeId ?? undefined, updatedAt: Date.now() });
    await audit(ctx, actorLite(user), { action: "CLEANING_UPDATED", module: "cleaning", entityType: "apartment", entityId: c.apartmentId, entityLabel: apt?.code ?? "", previousValue: { assignee: c.assigneeId ?? null }, newValue: { assignee: assigneeId }, apartmentId: c.apartmentId });
    if (assigneeId && assigneeId !== user.id) await notify(ctx, { type: "APARTMENT_NEEDS_CLEANING", title: `Cleaning assigned: ${apt?.code ?? ""}`, body: `${user.fullName} assigned you the cleaning of ${apt?.name ?? "an apartment"}.`, href: "/cleaning", targetUserIds: [assigneeId], actorId: user.id });
    return null;
  },
});

// ── Maintenance ──────────────────────────────────────────────
const mntArgs = {
  apartmentId: v.id("apartments"),
  title: v.string(),
  category: v.optional(v.string()),
  priority: v.optional(v.string()),
  description: nullable(v.string()),
  assigneeId: nullable(v.id("users")),
  cost: v.optional(v.number()),
  blocksApartment: v.optional(v.boolean()),
  startDate: nullable(v.string()),
  completionDate: nullable(v.string()),
};

export const maintenanceList = query({
  args: {},
  returns: v.array(v.any()),
  handler: async (ctx) => {
    await assertPermission(ctx, "maintenance.view");
    const rows = (await ctx.db.query("maintenanceTickets").collect()).sort((a, b) => b.createdAt - a.createdAt);
    return Promise.all(
      rows.map(async (m) => {
        const [apartment, assignee, reportedBy] = await Promise.all([ctx.db.get(m.apartmentId), m.assigneeId ? ctx.db.get(m.assigneeId) : null, ctx.db.get(m.reportedById)]);
        return { ...m, id: m._id, apartment: apartment ? { id: apartment._id, code: apartment.code, name: apartment.name } : null, assignee: assignee ? { id: assignee._id, fullName: assignee.fullName ?? "" } : null, reportedBy: { fullName: reportedBy?.fullName ?? "" } };
      })
    );
  },
});

export const createMaintenance = mutation({
  args: mntArgs,
  returns: v.object({ id: v.id("maintenanceTickets"), code: v.string() }),
  handler: async (ctx, data) => {
    const user = await assertPermission(ctx, "maintenance.manage");
    const settings = await getSettings(ctx);
    if (data.title.trim().length < 2) throw new AppError("Title is required", "VALIDATION", { fields: { title: "Title is required" } });
    const category = data.category ?? "GENERAL";
    const priority = data.priority ?? "MEDIUM";
    if (!MAINTENANCE_CATEGORIES.includes(category as (typeof MAINTENANCE_CATEGORIES)[number]) || !PRIORITIES.includes(priority as (typeof PRIORITIES)[number])) throw new AppError("Invalid category or priority", "VALIDATION");
    const apt = await ctx.db.get(data.apartmentId);
    if (!apt) throw new AppError("Apartment not found", "NOT_FOUND");
    const now = Date.now();
    const code = await nextCode(ctx, "maintenance");
    const blocks = !!data.blocksApartment;
    const id = await ctx.db.insert("maintenanceTickets", { code, apartmentId: apt._id, title: data.title.trim(), category, priority, description: data.description || undefined, assigneeId: data.assigneeId ?? undefined, reportedById: user.id, cost: Math.max(0, data.cost ?? 0), status: "REPORTED", blocksApartment: blocks, startDate: data.startDate || undefined, completionDate: data.completionDate || undefined, createdAt: now, updatedAt: now });
    const actor = actorLite(user);
    if (blocks && data.startDate && data.completionDate) {
      const conflicts = await findConflicts(ctx, apt._id, data.startDate, data.completionDate);
      if (conflicts.length) throw new AppError(`Not available: ${conflicts.map((c) => c.label).join(", ")}`, "CONFLICT", { conflicts });
      const blockId = await ctx.db.insert("apartmentBlocks", { apartmentId: apt._id, startDate: data.startDate, endDate: data.completionDate, reason: `Maintenance: ${data.title}`, type: "MAINTENANCE", source: "MAINTENANCE", maintenanceId: id, pendingApproval: false, releaseOnCleaning: false, createdById: user.id, createdAt: now });
      await ctx.db.patch(id, { blockId });
      await inventoryEvent(ctx, actor, { apartmentId: apt._id, action: "MAINTENANCE_STARTED", startDate: data.startDate, endDate: data.completionDate, previousState: "AVAILABLE", newState: "BLOCKED:MAINTENANCE", reason: data.title, source: "MAINTENANCE", blockId });
    }
    await ctx.db.patch(apt._id, { maintenanceStatus: blocks ? "BLOCKED" : "ISSUE", updatedAt: now });
    await audit(ctx, actor, { action: "MAINTENANCE_CREATED", module: "maintenance", entityType: "maintenance", entityId: id, entityLabel: `${code} · ${data.title}`, newValue: { apartment: apt.code, priority, blocks }, apartmentId: apt._id });
    await notify(ctx, { type: "MAINTENANCE_ALERT", title: `Maintenance: ${apt.code}`, body: `${user.fullName} reported "${data.title}" (${priority.toLowerCase()} priority).`, href: "/maintenance", actorId: user.id, targetUserIds: [data.assigneeId ?? null], priority: priority === "URGENT" || priority === "HIGH" ? "HIGH" : "NORMAL" });
    if (blocks) await syncApartmentStatus(ctx, apt._id, settings.timezone);
    return { id, code };
  },
});

export const updateMaintenance = mutation({
  args: { id: v.id("maintenanceTickets"), status: v.optional(v.string()), title: v.optional(v.string()), category: v.optional(v.string()), priority: v.optional(v.string()), description: nullable(v.string()), assigneeId: nullable(v.id("users")), cost: v.optional(v.number()), blocksApartment: v.optional(v.boolean()), startDate: nullable(v.string()), completionDate: nullable(v.string()) },
  returns: v.null(),
  handler: async (ctx, { id, status, ...data }) => {
    const user = await assertPermission(ctx, "maintenance.manage");
    const settings = await getSettings(ctx);
    const t = await ctx.db.get(id);
    if (!t) throw new AppError("Ticket not found", "NOT_FOUND");
    if (status && !MAINTENANCE_STATUSES.includes(status as (typeof MAINTENANCE_STATUSES)[number])) throw new AppError("Invalid status", "VALIDATION");
    const apt = await ctx.db.get(t.apartmentId);
    const now = Date.now();
    const patch: Record<string, unknown> = { updatedAt: now };
    for (const [k, val] of Object.entries(data)) if (val !== undefined) patch[k] = val === null || val === "" ? undefined : val;
    if (status) patch.status = status;
    await ctx.db.patch(id, patch);
    const actor = actorLite(user);
    if (status === "COMPLETED") {
      if (t.blockId) {
        const blk = await ctx.db.get(t.blockId);
        if (blk) {
          await ctx.db.delete(blk._id);
          await inventoryEvent(ctx, actor, { apartmentId: t.apartmentId, action: "MAINTENANCE_COMPLETED", startDate: blk.startDate, endDate: blk.endDate, previousState: "BLOCKED:MAINTENANCE", newState: "AVAILABLE", reason: t.title, source: "MAINTENANCE" });
        }
        await ctx.db.patch(id, { blockId: undefined });
      }
      const others = (await ctx.db.query("maintenanceTickets").withIndex("by_apartment_status", (q) => q.eq("apartmentId", t.apartmentId)).collect()).filter((m) => m.status !== "COMPLETED" && m._id !== id).length;
      await ctx.db.patch(t.apartmentId, { maintenanceStatus: others ? "ISSUE" : "OK", ...(apt?.status === "MAINTENANCE" ? { status: "AVAILABLE" } : {}), updatedAt: now });
      const cost = data.cost ?? t.cost;
      if (cost > 0) {
        const cat = await ctx.db.query("expenseCategories").withIndex("by_key", (q) => q.eq("key", "MAINTENANCE")).unique();
        if (cat) await ctx.db.insert("expenses", { code: await nextCode(ctx, "expense"), date: todayKey(settings.timezone), categoryId: cat._id, apartmentId: t.apartmentId, description: `${t.code} · ${t.title}`, amount: cost, paymentMethod: "CASH", isRecurring: false, addedById: user.id, notes: "Auto-created from completed maintenance ticket", updatedAt: now });
      }
    }
    await audit(ctx, actor, { action: "MAINTENANCE_UPDATED", module: "maintenance", entityType: "maintenance", entityId: id, entityLabel: `${t.code} · ${t.title}`, previousValue: { status: t.status, cost: t.cost }, newValue: { status: status ?? t.status, ...(data.cost !== undefined ? { cost: data.cost } : {}) }, apartmentId: t.apartmentId });
    if (data.assigneeId && data.assigneeId !== t.assigneeId && data.assigneeId !== user.id) await notify(ctx, { type: "MAINTENANCE_ALERT", title: `Maintenance assigned: ${apt?.code ?? ""}`, body: `${user.fullName} assigned you ${t.code}: ${t.title}.`, href: "/maintenance", targetUserIds: [data.assigneeId], actorId: user.id });
    await syncApartmentStatus(ctx, t.apartmentId, settings.timezone);
    return null;
  },
});

export const staffOptions = query({
  args: {},
  returns: v.array(v.object({ id: v.id("users"), fullName: v.string(), roleKey: v.string() })),
  handler: async (ctx) => {
    await requireActor(ctx);
    const roles = await ctx.db.query("roles").collect();
    const users = (await ctx.db.query("users").withIndex("by_status", (q) => q.eq("status", "ACTIVE")).collect()).filter((u) => !u.deletedAt && u.roleId);
    return users.map((u) => ({ id: u._id, fullName: u.fullName ?? u.email ?? "", roleKey: roles.find((r) => r._id === u.roleId)?.key ?? "" })).sort((a, b) => a.fullName.localeCompare(b.fullName));
  },
});

// ── Cleaning board payload ───────────────────────────────────
export const cleaningBoard = query({
  args: {},
  returns: v.any(),
  handler: async (ctx) => {
    const actor = await assertPermission(ctx, "cleaning.view");
    const settings = await getSettings(ctx);
    const today = todayKey(settings.timezone);
    const roles = await ctx.db.query("roles").collect();
    const apartments = (await ctx.db.query("apartments").withIndex("by_active", (q) => q.eq("isActive", true)).collect()).filter((a) => !a.deletedAt).sort((a, b) => a.code.localeCompare(b.code));
    const open = (await ctx.db.query("cleaningTasks").collect()).filter((c) => c.status === "NEEDS_CLEANING" || c.status === "IN_PROGRESS").sort((a, b) => a.createdAt - b.createdAt);
    const cleaners = (await ctx.db.query("users").withIndex("by_status", (q) => q.eq("status", "ACTIVE")).collect()).filter((u) => !u.deletedAt && u.roleId).map((u) => ({ id: u._id, fullName: u.fullName ?? "", isCleaner: roles.find((r) => r._id === u.roleId)?.key === "CLEANER" })).sort((a, b) => Number(b.isCleaner) - Number(a.isCleaner) || a.fullName.localeCompare(b.fullName));
    const outs = (await ctx.db.query("reservations").withIndex("by_checkOut", (q) => q.gte("checkOut", today).lte("checkOut", addDaysKey(today, 2))).collect()).filter((r) => r.status === "CHECKED_IN" || r.status === "CONFIRMED").sort((a, b) => a.checkOut.localeCompare(b.checkOut));
    const upcoming = await Promise.all(outs.map(async (r) => { const [a, c] = await Promise.all([ctx.db.get(r.apartmentId), ctx.db.get(r.customerId)]); return { id: r._id, code: r.code, checkOut: r.checkOut, apartment: a?.code ?? "", name: a?.name ?? "", guest: c?.fullName ?? "" }; }));
    const done = (await ctx.db.query("cleaningTasks").withIndex("by_status", (q) => q.eq("status", "READY")).collect()).filter((c) => (c.completedAt ?? 0) >= Date.now() - 7 * 86_400_000).sort((a, b) => (b.completedAt ?? 0) - (a.completedAt ?? 0)).slice(0, 30);
    const history = await Promise.all(done.map(async (h) => ({ id: h._id, apartment: (await ctx.db.get(h.apartmentId))?.code ?? "", completedAt: h.completedAt!, by: cleaners.find((c) => c.id === h.completedById)?.fullName ?? "—", notes: h.notes ?? null })));
    const nextByApt = new Map<string, { checkIn: string; guest: string }>();
    for (const n of (await ctx.db.query("reservations").withIndex("by_checkIn", (q) => q.gte("checkIn", today)).collect()).filter((r) => r.status === "CONFIRMED" || r.status === "PENDING").sort((a, b) => a.checkIn.localeCompare(b.checkIn))) if (!nextByApt.has(n.apartmentId)) nextByApt.set(n.apartmentId, { checkIn: n.checkIn, guest: (await ctx.db.get(n.customerId))?.fullName ?? "" });
    const rows = await Promise.all(apartments.map(async (a) => { const t = open.find((x) => x.apartmentId === a._id); const r = t?.reservationId ? await ctx.db.get(t.reservationId) : null; return { id: a._id, code: a.code, name: a.name, status: a.status, cleaningStatus: a.cleaningStatus, building: a.building ?? null, next: nextByApt.get(a._id) ?? null, task: t ? { id: t._id, status: t.status, assigneeId: t.assigneeId ?? null, notes: t.notes ?? null, reservationCode: r?.code ?? null, scheduledFor: t.scheduledFor ?? null, startedAt: t.startedAt ?? null } : null }; }));
    return { apartments: rows, staff: cleaners, upcoming, history, today, canManage: can(actor, "cleaning.manage"), me: actor.id };
  },
});

/** Tasks page: rows + filter options. */
export const tasksPage = query({
  args: {},
  returns: v.any(),
  handler: async (ctx) => {
    const actor = await assertPermission(ctx, "tasks.view");
    const cutoff14 = Date.now() - 14 * 86_400_000;
    const cutoff7 = Date.now() - 7 * 86_400_000;
    const all = (await ctx.db.query("tasks").collect()).filter((t) => t.status === "TODO" || t.status === "IN_PROGRESS" || (t.completedAt && t.completedAt >= cutoff14) || (t.status === "CANCELLED" && t.updatedAt >= cutoff7)).filter((t) => actor.isAdmin || can(actor, "tasks.manage") || t.assigneeId === actor.id || t.createdById === actor.id).sort((a, b) => b.createdAt - a.createdAt);
    const rows = await Promise.all(all.map(async (t) => { const [apartment, reservation, customer, assignee, createdBy] = await Promise.all([t.apartmentId ? ctx.db.get(t.apartmentId) : null, t.reservationId ? ctx.db.get(t.reservationId) : null, t.customerId ? ctx.db.get(t.customerId) : null, t.assigneeId ? ctx.db.get(t.assigneeId) : null, ctx.db.get(t.createdById)]); return { ...t, id: t._id, apartment: apartment ? { id: apartment._id, code: apartment.code } : null, reservation: reservation ? { id: reservation._id, code: reservation.code } : null, customer: customer ? { id: customer._id, fullName: customer.fullName } : null, assignee: assignee ? { id: assignee._id, fullName: assignee.fullName ?? "" } : null, createdBy: { fullName: createdBy?.fullName ?? "" } }; }));
    const apartments = (await activeApartments(ctx)).map((a) => ({ id: a._id, code: a.code, name: a.name }));
    const staff = (await ctx.db.query("users").withIndex("by_status", (q) => q.eq("status", "ACTIVE")).collect()).filter((u) => !u.deletedAt && u.roleId).map((u) => ({ id: u._id, fullName: u.fullName ?? "" })).sort((a, b) => a.fullName.localeCompare(b.fullName));
    return { rows, apartments, staff, me: actor.id, canManage: can(actor, "tasks.manage"), canCreate: can(actor, "tasks.create") || can(actor, "tasks.manage") };
  },
});

/** Maintenance page: tickets + options. */
export const maintenancePage = query({
  args: {},
  returns: v.any(),
  handler: async (ctx) => {
    const actor = await assertPermission(ctx, "maintenance.view");
    const rows = (await ctx.db.query("maintenanceTickets").collect()).sort((a, b) => b.createdAt - a.createdAt);
    const tickets = await Promise.all(rows.map(async (m) => { const [apartment, assignee, reportedBy, block] = await Promise.all([ctx.db.get(m.apartmentId), m.assigneeId ? ctx.db.get(m.assigneeId) : null, ctx.db.get(m.reportedById), m.blockId ? ctx.db.get(m.blockId) : null]); return { ...m, id: m._id, description: m.description ?? null, startDate: m.startDate ?? null, completionDate: m.completionDate ?? null, apartment: apartment ? { id: apartment._id, code: apartment.code, name: apartment.name } : { id: m.apartmentId, code: "?", name: "" }, assignee: assignee ? { id: assignee._id, fullName: assignee.fullName ?? "" } : null, reportedBy: { fullName: reportedBy?.fullName ?? "" }, block: block ? { startDate: block.startDate, endDate: block.endDate } : null }; }));
    const apartments = (await activeApartments(ctx)).map((a) => ({ id: a._id, code: a.code, name: a.name }));
    const staff = (await ctx.db.query("users").withIndex("by_status", (q) => q.eq("status", "ACTIVE")).collect()).filter((u) => !u.deletedAt && u.roleId).map((u) => ({ id: u._id, fullName: u.fullName ?? "" })).sort((a, b) => a.fullName.localeCompare(b.fullName));
    return { tickets, apartments, staff, canManage: can(actor, "maintenance.manage"), canReport: can(actor, "maintenance.report") || can(actor, "maintenance.manage"), showMoney: can(actor, "financials.view_revenue") || can(actor, "expenses.view") };
  },
});
