import prisma from '../config/db.js';
import type { PlanBody } from '../validators/plan.validator.js';
import { CORE_MODULE_KEYS } from '../config/modules.js';
import { logAudit, type AuditChange } from './auditLog.model.js';

// DB stores limits as flat columns; the API (matching blenaxis-super-admin's
// `Plan.limits: UsageLimits`) nests them — these two helpers translate.
function toRow(body: Partial<PlanBody>) {
  const { limits, includedModuleKeys, ...rest } = body;
  return {
    ...rest,
    ...(limits ? { maxUsers: limits.maxUsers, maxProjects: limits.maxProjects, storageGb: limits.storageGb } : {}),
    // Defensive, same as Organization.moduleKeys — a plan should never be
    // savable without the modules every org needs regardless of selection.
    ...(includedModuleKeys ? { includedModuleKeys: Array.from(new Set([...CORE_MODULE_KEYS, ...includedModuleKeys])) } : {}),
  };
}

function toApiShape<T extends { maxUsers: number; maxProjects: number; storageGb: number }>(plan: T) {
  const { maxUsers, maxProjects, storageGb, ...rest } = plan;
  return { ...rest, limits: { maxUsers, maxProjects, storageGb } };
}

export async function createPlan(data: PlanBody, actorId: number | undefined) {
  const plan = await prisma.plan.create({ data: toRow(data) as Parameters<typeof prisma.plan.create>[0]['data'] });
  await logAudit({
    target: { type: 'plan', id: plan.id, label: plan.name },
    actorId, action: 'plan.created',
  });
  return toApiShape(plan);
}

export async function getPlans() {
  const plans = await prisma.plan.findMany({
    orderBy: { priceMonthly: 'asc' },
    include: { _count: { select: { organizations: true } } },
  });
  return plans.map(({ _count, ...plan }) => ({ ...toApiShape(plan), organizationCount: _count.organizations }));
}

export async function getPlanById(id: string) {
  const plan = await prisma.plan.findUnique({ where: { id }, include: { _count: { select: { organizations: true } } } });
  if (!plan) return null;
  const { _count, ...rest } = plan;
  return { ...toApiShape(rest), organizationCount: _count.organizations };
}

export async function updatePlan(id: string, data: Partial<PlanBody>, actorId: number | undefined) {
  const before = await prisma.plan.findUnique({ where: { id } });
  const plan = await prisma.plan.update({ where: { id }, data: toRow(data) });

  const changes: AuditChange[] = [];
  if (before) {
    const fields: [string, unknown, unknown][] = [
      ['name', before.name, plan.name],
      ['code', before.code, plan.code],
      ['priceMonthly', before.priceMonthly, plan.priceMonthly],
      ['maxUsers', before.maxUsers, plan.maxUsers],
      ['maxProjects', before.maxProjects, plan.maxProjects],
      ['storageGb', before.storageGb, plan.storageGb],
      ['archived', before.archived, plan.archived],
    ];
    for (const [field, prev, next] of fields) {
      if (prev !== next) changes.push({ field, before: String(prev), after: String(next) });
    }
    if (before.includedModuleKeys.join(',') !== plan.includedModuleKeys.join(',')) {
      changes.push({ field: 'modules', before: before.includedModuleKeys.join(', '), after: plan.includedModuleKeys.join(', ') });
    }
  }

  await logAudit({
    target: { type: 'plan', id: plan.id, label: plan.name },
    actorId, action: 'plan.updated', changes,
  });
  return toApiShape(plan);
}
