import { createHash } from "node:crypto";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { isAbsolute, resolve } from "node:path";
import { env } from "../env.js";
import { prisma } from "./prisma.js";

function sanitizeFileName(fileName: string) {
  return fileName.replace(/[^a-zA-Z0-9._-]+/g, "_");
}

export function getStorageRoot() {
  return resolve(process.cwd(), "../../storage");
}

function resolveObjectKeyPath(objectKey: string) {
  return isAbsolute(objectKey) ? objectKey : resolve(getStorageRoot(), objectKey);
}

let cachedImageKitUrlEndpoint: string | null = null;

async function resolveImageKitUrlEndpoint() {
  if (cachedImageKitUrlEndpoint !== null) {
    return cachedImageKitUrlEndpoint || null;
  }

  const configuredEndpoint = String(env.IMAGEKIT_URL_ENDPOINT ?? "").trim();
  if (configuredEndpoint) {
    cachedImageKitUrlEndpoint = configuredEndpoint.replace(/\/+$/, "");
    return cachedImageKitUrlEndpoint;
  }

  const [userAvatarRow] = await prisma.$queryRaw<Array<{ avatar_url: string | null }>>`
    SELECT avatar_url
    FROM users
    WHERE avatar_url IS NOT NULL
      AND avatar_url LIKE 'http%'
      AND avatar_url LIKE '%imagekit%'
    ORDER BY updated_at DESC
    LIMIT 1
  `;

  const candidateUrl = String(userAvatarRow?.avatar_url ?? "").trim();
  if (!candidateUrl) {
    cachedImageKitUrlEndpoint = "";
    return null;
  }

  try {
    const parsedUrl = new URL(candidateUrl);
    const [firstPathSegment] = parsedUrl.pathname.split("/").filter(Boolean);
    if (!firstPathSegment) {
      cachedImageKitUrlEndpoint = "";
      return null;
    }

    cachedImageKitUrlEndpoint = `${parsedUrl.origin}/${firstPathSegment}`;
    return cachedImageKitUrlEndpoint;
  } catch {
    cachedImageKitUrlEndpoint = "";
    return null;
  }
}

async function resolveImageKitObjectUrl(objectKey: string) {
  if (/^https?:\/\//i.test(objectKey)) {
    return objectKey;
  }

  const urlEndpoint = await resolveImageKitUrlEndpoint();
  if (!urlEndpoint) {
    return null;
  }

  const normalizedPath = objectKey.startsWith("/") ? objectKey : `/${objectKey}`;
  return `${urlEndpoint}${normalizedPath}`;
}

export async function saveBinaryFile(input: {
  lawFirmId: string;
  caseId?: string | null;
  fileName: string;
  bytes: Buffer;
  kind: "uploads" | "generated";
}) {
  const baseDir = resolve(
    getStorageRoot(),
    input.kind,
    input.lawFirmId,
    input.caseId ?? "shared",
  );

  await mkdir(baseDir, { recursive: true });

  const storedFileName = `${Date.now()}_${sanitizeFileName(input.fileName)}`;
  const absolutePath = resolve(baseDir, storedFileName);

  await writeFile(absolutePath, input.bytes);

  return {
    absolutePath,
    relativeObjectKey: absolutePath.replace(`${getStorageRoot()}/`, ""),
    storedFileName,
    checksumSha256: createHash("sha256").update(input.bytes).digest("hex"),
  };
}

export async function readBinaryFile(input: {
  storageProvider: string;
  objectKey: string;
}) {
  if (input.storageProvider === "local_dev") {
    return readFile(resolveObjectKeyPath(input.objectKey));
  }

  if (input.storageProvider === "imagekit") {
    const objectUrl = await resolveImageKitObjectUrl(input.objectKey);
    if (!objectUrl) {
      throw new Error("ImageKit object key is not directly readable.");
    }

    const response = await fetch(objectUrl);
    if (!response.ok) {
      throw new Error(`Unable to read remote file (${response.status}).`);
    }

    return Buffer.from(await response.arrayBuffer());
  }

  throw new Error(`Unsupported storage provider: ${input.storageProvider}`);
}

export async function deleteBinaryFile(input: {
  storageProvider: string;
  objectKey: string;
}) {
  if (input.storageProvider === "local_dev") {
    await rm(resolveObjectKeyPath(input.objectKey), { force: true });
    return;
  }

  throw new Error(`Unsupported storage provider: ${input.storageProvider}`);
}
