import "server-only";

import { mkdir, readFile, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
import path from "node:path";
import { createHash, randomUUID } from "node:crypto";
import { MAX_UPLOAD_BYTES } from "@/lib/constants";
import { orphanUploadFolderName, type OrphanUploadIdentity } from "@/lib/orphan-upload-path";

const allowed = {
  pdf: "application/pdf",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  png: "image/png",
  webp: "image/webp"
} as const;

const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

export type UploadKind = "documents" | "receipts";

export type StoredPrivateFile = {
  storedName: string;
  originalName: string;
  extension: keyof typeof allowed;
  mimeType: (typeof allowed)[keyof typeof allowed];
  sizeBytes: number;
  sha256: string;
  absolutePath: string;
};

function uploadRoot(): string {
  return path.resolve(/*turbopackIgnore: true*/ process.cwd(), "public", "uploads");
}

function legacyUploadRoot(): string | null {
  const configured = process.env.UPLOAD_ROOT?.trim();
  if (!configured || !path.isAbsolute(configured)) return null;
  const root = path.resolve(/*turbopackIgnore: true*/ configured);
  return root === uploadRoot() ? null : root;
}

function assertUuid(value: string, label: string) {
  if (!uuidPattern.test(value)) throw new Error(`Invalid ${label}`);
}

function isWithin(root: string, candidate: string): boolean {
  const relative = path.relative(root, candidate);
  return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
}

function orphanContainerRoot(): string {
  const root = uploadRoot();
  const directory = path.resolve(/*turbopackIgnore: true*/ root, "orphans");
  if (!isWithin(root, directory)) throw new Error("Invalid orphan upload path");
  return directory;
}

function namedOrphanUploadRoot(orphan: OrphanUploadIdentity): string {
  assertUuid(orphan.orphanId, "orphan reference");
  const root = orphanContainerRoot();
  const directory = path.resolve(
    /*turbopackIgnore: true*/ root,
    orphanUploadFolderName(orphan)
  );
  if (!isWithin(root, directory)) throw new Error("Invalid orphan upload path");
  return directory;
}

function legacyOrphanUploadRoot(orphanId: string): string {
  assertUuid(orphanId, "orphan reference");
  const root = orphanContainerRoot();
  const directory = path.resolve(/*turbopackIgnore: true*/ root, orphanId);
  if (!isWithin(root, directory)) throw new Error("Invalid legacy orphan upload path");
  return directory;
}

function pendingOrphanUploadRoot(orphanId: string): string {
  assertUuid(orphanId, "orphan reference");
  const root = uploadRoot();
  const pendingRoot = path.resolve(/*turbopackIgnore: true*/ root, ".pending");
  const directory = path.resolve(/*turbopackIgnore: true*/ pendingRoot, orphanId);
  if (!isWithin(pendingRoot, directory)) throw new Error("Invalid pending orphan upload path");
  return directory;
}

function uploadDirectory(orphanRoot: string, kind: UploadKind): string {
  const directory = path.resolve(/*turbopackIgnore: true*/ orphanRoot, kind);
  if (!isWithin(orphanRoot, directory)) throw new Error("Invalid upload directory");
  return directory;
}

function filePath(orphanRoot: string, kind: UploadKind, storedName: string, extension: string): string {
  assertUuid(storedName, "file reference");
  if (!(extension in allowed)) throw new Error("Invalid file extension");
  const directory = uploadDirectory(orphanRoot, kind);
  const absolutePath = path.resolve(/*turbopackIgnore: true*/ directory, `${storedName}.${extension}`);
  if (!isWithin(directory, absolutePath)) throw new Error("Invalid file path");
  return absolutePath;
}

async function pathExists(candidate: string): Promise<boolean> {
  try {
    await stat(/*turbopackIgnore: true*/ candidate);
    return true;
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
    throw error;
  }
}

async function ensureUploadFolders(orphanRoot: string) {
  await Promise.all([
    mkdir(/*turbopackIgnore: true*/ uploadDirectory(orphanRoot, "documents"), { recursive: true }),
    mkdir(/*turbopackIgnore: true*/ uploadDirectory(orphanRoot, "receipts"), { recursive: true })
  ]);
}

function legacyFilePath(storedName: string, extension: string): string | null {
  const root = legacyUploadRoot();
  if (!root) return null;
  assertUuid(storedName, "file reference");
  if (!(extension in allowed)) throw new Error("Invalid file extension");
  const absolutePath = path.resolve(/*turbopackIgnore: true*/ root, `${storedName}.${extension}`);
  return isWithin(root, absolutePath) ? absolutePath : null;
}

function extensionOf(name: string): keyof typeof allowed | null {
  const extension = path.extname(name).slice(1).toLowerCase();
  return extension in allowed ? (extension as keyof typeof allowed) : null;
}

function signatureMatches(buffer: Buffer, extension: keyof typeof allowed): boolean {
  if (extension === "pdf") return buffer.subarray(0, 5).toString("ascii") === "%PDF-";
  if (extension === "jpg" || extension === "jpeg") {
    return buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff;
  }
  if (extension === "png") {
    return buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
  }
  return buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP";
}

async function savePrivateUploadAtRoot(
  file: File,
  orphanRoot: string,
  options: { kind: UploadKind; imageOnly?: boolean }
): Promise<StoredPrivateFile> {
  if (file.size <= 0 || file.size > MAX_UPLOAD_BYTES) {
    throw new Error(`حجم الملف يجب ألا يتجاوز ${Math.floor(MAX_UPLOAD_BYTES / 1024 / 1024)} ميغابايت`);
  }

  const extension = extensionOf(file.name);
  if (!extension || allowed[extension] !== file.type) throw new Error("نوع الملف غير مسموح");
  if (options.imageOnly && extension === "pdf") throw new Error("يجب رفع صورة بصيغة JPG أو PNG أو WEBP");

  const buffer = Buffer.from(await file.arrayBuffer());
  if (!signatureMatches(buffer, extension)) throw new Error("محتوى الملف لا يطابق نوعه");

  await ensureUploadFolders(orphanRoot);
  const storedName = randomUUID();
  const absolutePath = filePath(orphanRoot, options.kind, storedName, extension);
  await writeFile(/*turbopackIgnore: true*/ absolutePath, buffer, { flag: "wx", mode: 0o600 });

  return {
    storedName,
    originalName: path.basename(file.name).slice(0, 240),
    extension,
    mimeType: allowed[extension],
    sizeBytes: buffer.length,
    sha256: createHash("sha256").update(buffer).digest("hex"),
    absolutePath
  };
}

async function moveOrphanUploadRoot(source: string, destination: string) {
  if (source === destination) {
    await ensureUploadFolders(destination);
    return;
  }
  if (!(await pathExists(source))) {
    await ensureUploadFolders(destination);
    return;
  }
  if (await pathExists(destination)) {
    throw new Error("مجلد ملفات اليتيم موجود مسبقاً");
  }

  await mkdir(/*turbopackIgnore: true*/ path.dirname(destination), { recursive: true });
  await rename(/*turbopackIgnore: true*/ source, destination);
  try {
    await ensureUploadFolders(destination);
  } catch (error) {
    await rename(/*turbopackIgnore: true*/ destination, source).catch(() => undefined);
    throw error;
  }
}

export async function ensureOrphanUploadFolders(orphan: OrphanUploadIdentity) {
  await ensureUploadFolders(namedOrphanUploadRoot(orphan));
}

export async function ensurePendingOrphanUploadFolders(orphanId: string) {
  await ensureUploadFolders(pendingOrphanUploadRoot(orphanId));
}

export async function savePrivateUpload(
  file: File,
  options: { orphan: OrphanUploadIdentity; kind: UploadKind; imageOnly?: boolean }
): Promise<StoredPrivateFile> {
  return savePrivateUploadAtRoot(file, namedOrphanUploadRoot(options.orphan), options);
}

export async function savePendingPrivateUpload(
  file: File,
  options: { orphanId: string; kind: UploadKind; imageOnly?: boolean }
): Promise<StoredPrivateFile> {
  return savePrivateUploadAtRoot(file, pendingOrphanUploadRoot(options.orphanId), options);
}

export async function finalizePendingOrphanUploads(
  orphanId: string,
  orphan: OrphanUploadIdentity
) {
  if (orphanId !== orphan.orphanId) throw new Error("Invalid orphan upload identity");
  await moveOrphanUploadRoot(pendingOrphanUploadRoot(orphanId), namedOrphanUploadRoot(orphan));
}

export async function renameOrphanUploadTree(
  previous: OrphanUploadIdentity,
  next: OrphanUploadIdentity
) {
  if (previous.orphanId !== next.orphanId) throw new Error("Invalid orphan upload identity");
  const namedSource = namedOrphanUploadRoot(previous);
  const source = (await pathExists(namedSource))
    ? namedSource
    : legacyOrphanUploadRoot(previous.orphanId);
  await moveOrphanUploadRoot(source, namedOrphanUploadRoot(next));
}

export async function readPrivateUpload(
  orphan: OrphanUploadIdentity,
  kind: UploadKind,
  storedName: string,
  extension: string
): Promise<Buffer> {
  const candidates = [
    filePath(namedOrphanUploadRoot(orphan), kind, storedName, extension),
    filePath(legacyOrphanUploadRoot(orphan.orphanId), kind, storedName, extension),
    legacyFilePath(storedName, extension)
  ].filter((candidate): candidate is string => Boolean(candidate));

  let missingError: NodeJS.ErrnoException | null = null;
  for (const candidate of candidates) {
    try {
      return await readFile(/*turbopackIgnore: true*/ candidate);
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
      missingError = error as NodeJS.ErrnoException;
    }
  }
  throw missingError ?? new Error("Upload file not found");
}

export async function movePrivateUploadByReference(
  sourceOrphan: OrphanUploadIdentity,
  destinationOrphan: OrphanUploadIdentity,
  storedName: string,
  extension: string,
  kind: UploadKind
): Promise<boolean> {
  const destinationRoot = namedOrphanUploadRoot(destinationOrphan);
  await ensureUploadFolders(destinationRoot);
  const destination = filePath(destinationRoot, kind, storedName, extension);
  const candidates = [
    filePath(namedOrphanUploadRoot(sourceOrphan), kind, storedName, extension),
    filePath(legacyOrphanUploadRoot(sourceOrphan.orphanId), kind, storedName, extension),
    legacyFilePath(storedName, extension)
  ].filter((candidate): candidate is string => Boolean(candidate));
  const source = (await Promise.all(candidates.map(async (candidate) => ({
    candidate,
    exists: await pathExists(candidate)
  })))).find((item) => item.exists)?.candidate;

  if (!source) return false;
  if (source === destination) return true;
  if (await pathExists(destination)) throw new Error("Target upload file already exists");

  const buffer = await readFile(/*turbopackIgnore: true*/ source);
  await writeFile(/*turbopackIgnore: true*/ destination, buffer, { flag: "wx", mode: 0o600 });
  try {
    await unlink(/*turbopackIgnore: true*/ source);
  } catch (error) {
    await unlink(/*turbopackIgnore: true*/ destination).catch(() => undefined);
    throw error;
  }
  return true;
}

export async function removePrivateUpload(absolutePath: string) {
  const resolved = path.resolve(/*turbopackIgnore: true*/ absolutePath);
  const roots = [uploadRoot(), legacyUploadRoot()].filter((root): root is string => Boolean(root));
  if (!roots.some((root) => isWithin(root, resolved))) return;
  await unlink(/*turbopackIgnore: true*/ resolved).catch(() => undefined);
}

export async function removePrivateUploadByReference(
  orphan: OrphanUploadIdentity,
  storedName: string,
  extension: string,
  kind?: UploadKind
): Promise<boolean> {
  try {
    const kinds: UploadKind[] = kind ? [kind] : ["documents", "receipts"];
    const orphanRoots = [
      namedOrphanUploadRoot(orphan),
      legacyOrphanUploadRoot(orphan.orphanId)
    ];
    const candidates = orphanRoots.flatMap((root) =>
      kinds.map((candidateKind) => filePath(root, candidateKind, storedName, extension))
    );
    const legacyPath = legacyFilePath(storedName, extension);
    if (legacyPath) candidates.push(legacyPath);

    let succeeded = true;
    for (const candidate of candidates) {
      try {
        await unlink(/*turbopackIgnore: true*/ candidate);
      } catch (error) {
        if ((error as NodeJS.ErrnoException).code !== "ENOENT") succeeded = false;
      }
    }
    return succeeded;
  } catch {
    return false;
  }
}

export async function removePendingOrphanUploadTree(orphanId: string): Promise<boolean> {
  try {
    await rm(/*turbopackIgnore: true*/ pendingOrphanUploadRoot(orphanId), {
      recursive: true,
      force: true
    });
    return true;
  } catch {
    return false;
  }
}

export async function removeOrphanUploadTree(orphan: OrphanUploadIdentity): Promise<boolean> {
  try {
    const directories = [
      namedOrphanUploadRoot(orphan),
      legacyOrphanUploadRoot(orphan.orphanId),
      pendingOrphanUploadRoot(orphan.orphanId)
    ];
    let succeeded = true;
    for (const directory of directories) {
      try {
        await rm(/*turbopackIgnore: true*/ directory, { recursive: true, force: true });
      } catch {
        succeeded = false;
      }
    }
    return succeeded;
  } catch {
    return false;
  }
}
