import "server-only";

import { createHash, randomUUID } from "node:crypto";
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
import path from "node:path";

export const MAX_CONTRACT_PDF_BYTES = 20 * 1024 * 1024;

const CONTRACT_STORAGE_ROOT = path.join(process.cwd(), "storage", "contracts");

export type StoredContractFile = {
  fileName: string;
  filePath: string;
  mimeType: string;
  sizeBytes: number;
  checksumSha256: string;
};

function normalizeExtension(fileName: string, mimeType: string) {
  const normalized = fileName.trim().toLowerCase();
  if (normalized.endsWith(".pdf")) {
    return "pdf";
  }

  if (normalized.endsWith(".xml")) {
    return "xml";
  }

  if (normalized.endsWith(".jpg") || normalized.endsWith(".jpeg")) {
    return "jpg";
  }

  if (normalized.endsWith(".png")) {
    return "png";
  }

  if (mimeType === "application/pdf") {
    return "pdf";
  }

  if (mimeType === "application/xml" || mimeType === "text/xml") {
    return "xml";
  }

  return "bin";
}

function sanitizeFileName(fileName: string, extension: string) {
  const compact = fileName.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-");
  const fallback = compact || `contract-document.${extension}`;
  return fallback.endsWith(`.${extension}`) ? fallback : `${fallback}.${extension}`;
}

export async function storeContractDocumentFile(
  userId: string,
  contractId: string,
  originalFileName: string,
  mimeType: string,
  fileBuffer: Buffer,
): Promise<StoredContractFile> {
  const extension = normalizeExtension(originalFileName, mimeType);
  const safeOriginalName = sanitizeFileName(originalFileName, extension);
  const userContractDir = path.join(CONTRACT_STORAGE_ROOT, userId, contractId);
  const storedFileName = `${Date.now()}-${randomUUID()}.${extension}`;
  const absolutePath = path.join(userContractDir, storedFileName);

  await mkdir(userContractDir, { recursive: true });
  await writeFile(absolutePath, fileBuffer);

  return {
    fileName: safeOriginalName,
    filePath: path.join("storage", "contracts", userId, contractId, storedFileName).replace(/\\/g, "/"),
    mimeType: mimeType || "application/octet-stream",
    sizeBytes: fileBuffer.byteLength,
    checksumSha256: createHash("sha256").update(fileBuffer).digest("hex"),
  };
}

export async function removeStoredContractDocumentFile(relativeFilePath: string | null | undefined) {
  if (!relativeFilePath) {
    return;
  }

  const storageRoot = path.resolve(process.cwd(), "storage");
  const absolutePath = path.resolve(process.cwd(), relativeFilePath);

  if (!absolutePath.startsWith(`${storageRoot}${path.sep}`)) {
    return;
  }

  try {
    await unlink(absolutePath);
  } catch {
    // Ignore cleanup errors.
  }
}

export async function readStoredContractDocumentFile(relativeFilePath: string, maxBytes = MAX_CONTRACT_PDF_BYTES) {
  const storageRoot = path.resolve(process.cwd(), "storage");
  const absolutePath = path.resolve(process.cwd(), relativeFilePath);

  if (!absolutePath.startsWith(`${storageRoot}${path.sep}`)) {
    throw new Error("CONTRACT_DOCUMENT_INVALID_PATH");
  }

  try {
    const buffer = await readFile(absolutePath);

    if (!buffer.byteLength) {
      throw new Error("CONTRACT_DOCUMENT_EMPTY");
    }

    if (buffer.byteLength > maxBytes) {
      throw new Error("CONTRACT_DOCUMENT_TOO_LARGE");
    }

    return buffer;
  } catch (error) {
    if (
      error &&
      typeof error === "object" &&
      "code" in error &&
      (error as NodeJS.ErrnoException).code === "ENOENT"
    ) {
      throw new Error("CONTRACT_DOCUMENT_NOT_FOUND");
    }

    throw error;
  }
}
