import { BlueprintTarget, PermissionKey } from "@capakraken/shared";
import type { BlueprintFieldDefinition } from "@capakraken/shared";
import { z } from "zod";
import type { TRPCContext } from "../trpc.js";
import { requirePermission } from "../trpc.js";

type ImportExportReadContext = Pick<TRPCContext, "db">;

type ImportExportMutationContext = ImportExportReadContext & {
  permissions: Set<PermissionKey>;
};

type ImportRow = Record<string, string>;

export const importCsvInputSchema = z.object({
  entityType: z.enum(["resources", "projects", "allocations"]),
  rows: z.array(z.record(z.string(), z.string())),
  dryRun: z.boolean().default(true),
});

type ImportCsvInput = z.infer<typeof importCsvInputSchema>;

function escapeCsvValue(value: unknown): string {
  const serialized = value === null || value === undefined ? "" : String(value);
  return serialized.includes(",") || serialized.includes('"') || serialized.includes("\n")
    ? `"${serialized.replace(/"/g, '""')}"`
    : serialized;
}

function resolveVisibleBlueprintFields(fieldDefs: unknown): BlueprintFieldDefinition[] {
  return (fieldDefs as BlueprintFieldDefinition[]).filter((field) => field.showInList);
}

function buildCsv(headers: unknown[], rows: unknown[][]) {
  return [headers.map(escapeCsvValue).join(","), ...rows.map((row) => row.map(escapeCsvValue).join(","))].join("\n");
}

export async function exportResourcesCsv(ctx: ImportExportReadContext) {
  const [resources, globalBlueprints] = await Promise.all([
    ctx.db.resource.findMany({
      where: { isActive: true },
      orderBy: { eid: "asc" },
    }),
    ctx.db.blueprint.findMany({
      where: { target: BlueprintTarget.RESOURCE, isGlobal: true, isActive: true },
      select: { fieldDefs: true },
    }),
  ]);

  const customDefs = globalBlueprints.flatMap((blueprint) =>
    resolveVisibleBlueprintFields(blueprint.fieldDefs),
  );
  const headers = [
    "eid",
    "displayName",
    "email",
    "chapter",
    "lcrCents",
    "ucrCents",
    "currency",
    "chargeabilityTarget",
    ...customDefs.map((field) => field.label),
  ];

  const rows = resources.map((resource) => {
    const dynamicFields = (resource.dynamicFields as Record<string, unknown>) ?? {};
    return [
      resource.eid,
      resource.displayName,
      resource.email,
      resource.chapter ?? "",
      resource.lcrCents,
      resource.ucrCents,
      resource.currency,
      resource.chargeabilityTarget,
      ...customDefs.map((field) => dynamicFields[field.key] ?? ""),
    ];
  });

  return buildCsv(headers, rows);
}

export async function exportProjectsCsv(ctx: ImportExportReadContext) {
  const [projects, globalBlueprints] = await Promise.all([
    ctx.db.project.findMany({ orderBy: { shortCode: "asc" } }),
    ctx.db.blueprint.findMany({
      where: { target: BlueprintTarget.PROJECT, isGlobal: true, isActive: true },
      select: { fieldDefs: true },
    }),
  ]);

  const customDefs = globalBlueprints.flatMap((blueprint) =>
    resolveVisibleBlueprintFields(blueprint.fieldDefs),
  );
  const headers = [
    "shortCode",
    "name",
    "orderType",
    "status",
    "budgetCents",
    "startDate",
    "endDate",
    "winProbability",
    ...customDefs.map((field) => field.label),
  ];

  const rows = projects.map((project) => {
    const dynamicFields = (project.dynamicFields as Record<string, unknown>) ?? {};
    return [
      project.shortCode,
      project.name,
      project.orderType,
      project.status,
      project.budgetCents,
      project.startDate.toISOString().split("T")[0],
      project.endDate.toISOString().split("T")[0],
      project.winProbability,
      ...customDefs.map((field) => dynamicFields[field.key] ?? ""),
    ];
  });

  return buildCsv(headers, rows);
}

async function importResourceRow(ctx: ImportExportMutationContext, row: ImportRow) {
  const existing = await ctx.db.resource.findFirst({
    where: { eid: row["eid"] ?? "" },
  });

  if (!existing) {
    return { updated: false, error: "New resource creation via import requires full data" };
  }

  await ctx.db.resource.update({
    where: { id: existing.id },
    data: {
      displayName: row["displayName"] ?? existing.displayName,
      email: row["email"] ?? existing.email,
      chapter: row["chapter"] ?? existing.chapter,
      lcrCents: row["lcrCents"] ? parseInt(row["lcrCents"], 10) : existing.lcrCents,
    },
  });

  return { updated: true, error: null };
}

export async function importCsv(ctx: ImportExportMutationContext, input: ImportCsvInput) {
  requirePermission(ctx, PermissionKey.IMPORT_DATA);

  const results = {
    total: input.rows.length,
    created: 0,
    updated: 0,
    errors: [] as { row: number; message: string }[],
    dryRun: input.dryRun,
  };

  if (input.dryRun) {
    return { ...results, message: `Dry run: ${input.rows.length} rows validated` };
  }

  for (let index = 0; index < input.rows.length; index += 1) {
    const row = input.rows[index];
    if (!row) {
      continue;
    }

    try {
      if (input.entityType === "resources") {
        const outcome = await importResourceRow(ctx, row);
        if (outcome.updated) {
          results.updated += 1;
        } else if (outcome.error) {
          results.errors.push({ row: index + 1, message: outcome.error });
        }
      }
    } catch (error) {
      results.errors.push({
        row: index + 1,
        message: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  await ctx.db.auditLog.create({
    data: {
      entityType: input.entityType,
      entityId: "bulk-import",
      action: "IMPORT",
      changes: { summary: results },
    },
  });

  return results;
}
