/* eslint-disable @typescript-eslint/no-explicit-any -- Convex table-generic helpers; runtime validators enforce the shapes */
import type { Doc, Id } from "../_generated/dataModel";
import type { MutationCtx, QueryCtx } from "../_generated/server";

/**
 * Small DTO builders shared by the read side. Every list/detail query returns
 * plain JSON with `id` (alias of `_id`), day strings ("YYYY-MM-DD") for
 * calendar dates and epoch-ms numbers for instants; the web layer hydrates
 * those into Date objects where components expect them.
 */
type Ctx = QueryCtx | MutationCtx;

export function withId<T extends { _id: Id<any> }>(d: T): T & { id: T["_id"] } {
  return { ...d, id: d._id };
}

export async function userLite(ctx: Ctx, id: Id<"users"> | null | undefined) {
  if (!id) return null;
  const u = await ctx.db.get(id);
  return u ? { id: u._id, fullName: u.fullName ?? "", code: u.code ?? null } : null;
}

export async function apartmentLite(ctx: Ctx, id: Id<"apartments"> | null | undefined) {
  if (!id) return null;
  const a = await ctx.db.get(id);
  return a ? { id: a._id, code: a.code, name: a.name, building: a.building ?? null, city: a.city, status: a.status, cleaningStatus: a.cleaningStatus, maxGuests: a.maxGuests, basePrice: a.basePrice } : null;
}

export async function customerLite(ctx: Ctx, id: Id<"customers"> | null | undefined) {
  if (!id) return null;
  const c = await ctx.db.get(id);
  return c ? { id: c._id, code: c.code, fullName: c.fullName, phone: c.phone, email: c.email ?? null, riskLevel: c.riskLevel, verificationStatus: c.verificationStatus, isBlacklisted: c.isBlacklisted } : null;
}

/** Reservation row with the relations every list/table needs. */
export async function reservationRow(ctx: Ctx, r: Doc<"reservations">) {
  const [customer, apartment, createdBy, assignedTo] = await Promise.all([customerLite(ctx, r.customerId), apartmentLite(ctx, r.apartmentId), userLite(ctx, r.createdById), userLite(ctx, r.assignedToId)]);
  return { ...withId(r), customer, apartment, createdBy, assignedTo };
}

/** Cache-friendly batch loader for lookups inside loops. */
export function loader<T extends "users" | "apartments" | "customers">(ctx: Ctx, table: T) {
  const cache = new Map<string, Doc<T> | null>();
  return async (id: Id<T> | null | undefined): Promise<Doc<T> | null> => {
    if (!id) return null;
    if (cache.has(id)) return cache.get(id)!;
    const d = (await ctx.db.get(id)) as Doc<T> | null;
    cache.set(id, d);
    return d;
  };
}

export const isRevenue = (status: string) => status === "CONFIRMED" || status === "CHECKED_IN" || status === "CHECKED_OUT";
export const pct = (part: number, total: number) => (total > 0 ? Math.round((part / total) * 1000) / 10 : 0);
export const delta = (current: number, previous: number): number | null => (previous === 0 ? (current === 0 ? 0 : null) : Math.round(((current - previous) / Math.abs(previous)) * 1000) / 10);
