import fs from 'fs';
import path from 'path';
import { format } from 'util';
import { Storage } from '@google-cloud/storage';
import type { Request, Response } from 'express';
import config from '@/shared/config';
import processFile from '@/middlewares/upload';

interface UploadValidations {
  entity?: string | null;
  maxFileSize?: number | null;
  folderIncludesAuthenticationUid?: boolean;
}

function ensureDirectoryExistence(filePath: string): void {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
}

/**
 * Resolves a client-supplied relative path against the upload dir and rejects
 * anything that escapes it (path traversal via `..`/absolute paths). Returns the
 * absolute path when safe, otherwise null.
 */
function resolveWithinUploadDir(relativePath: string): string | null {
  const baseDir = path.resolve(config.uploadDir);
  const resolved = path.resolve(baseDir, relativePath);
  if (resolved !== baseDir && !resolved.startsWith(baseDir + path.sep)) {
    return null;
  }
  return resolved;
}

function initGCloud() {
  const hash = config.gcloud.hash;
  const privateKey = (process.env.GC_PRIVATE_KEY || '').replace(/\\\n/g, '\n');

  const storage = new Storage({
    projectId: process.env.GC_PROJECT_ID,
    credentials: {
      client_email: process.env.GC_CLIENT_EMAIL,
      private_key: privateKey,
    },
  });

  const bucket = storage.bucket(config.gcloud.bucket);
  return { hash, bucket, processFile };
}

/**
 * Local-disk upload (development only). Uses the shared multer middleware
 * (`processFile`) to read the file, then writes it under the upload dir.
 */
function uploadLocal(folder: string, validations: UploadValidations = {}) {
  return async (req: Request, res: Response): Promise<void> => {
    if (!req.currentUser) {
      res.sendStatus(403);
      return;
    }

    if (validations.entity) {
      res.sendStatus(403);
      return;
    }

    try {
      await processFile(req, res);

      if (!req.file) {
        res.sendStatus(400);
        return;
      }

      const filename = String(req.body?.filename ?? '');
      if (!filename) {
        res.sendStatus(500);
        return;
      }

      const privateUrl = resolveWithinUploadDir(path.join(folder, filename));
      if (!privateUrl) {
        res.sendStatus(403);
        return;
      }
      ensureDirectoryExistence(privateUrl);
      fs.writeFileSync(privateUrl, req.file.buffer);
      res.sendStatus(200);
    } catch (error) {
      res.status(500).send(String(error));
    }
  };
}

function downloadLocal(req: Request, res: Response): void {
  const privateUrl = req.query.privateUrl;
  if (!privateUrl) {
    res.sendStatus(404);
    return;
  }
  const resolved = resolveWithinUploadDir(String(privateUrl));
  if (!resolved) {
    res.sendStatus(403);
    return;
  }
  res.download(resolved);
}

async function uploadGCloud(
  folder: string,
  req: Request,
  res: Response,
): Promise<void> {
  try {
    const { hash, bucket } = initGCloud();
    await processFile(req, res);

    if (!req.file) {
      res.status(400).send({ message: 'Please upload a file!' });
      return;
    }

    const buffer = req.file.buffer;
    const filename = String(req.body?.filename ?? '');
    const objectPath = `${hash}/${folder}/${filename}`;
    const blob = bucket.file(objectPath);

    const blobStream = blob.createWriteStream({ resumable: false });

    blobStream.on('error', (err: Error) => {
      res.status(500).send({ message: err.message });
    });

    blobStream.on('finish', () => {
      const publicUrl = format(
        `https://storage.googleapis.com/${bucket.name}/${blob.name}`,
      );
      res.status(200).send({
        message: 'Uploaded the file successfully: ' + objectPath,
        url: publicUrl,
      });
    });

    blobStream.end(buffer);
  } catch (error) {
    res.status(500).send({ message: `Could not upload the file. ${error}` });
  }
}

async function downloadGCloud(req: Request, res: Response): Promise<void> {
  try {
    const { hash, bucket } = initGCloud();

    const privateUrl = String(req.query.privateUrl ?? '');
    const filePath = `${hash}/${privateUrl}`;
    const file = bucket.file(filePath);
    const [fileExists] = await file.exists();

    if (fileExists) {
      file.createReadStream().pipe(res);
    } else {
      res.status(404).send({ message: 'Could not download the file.' });
    }
  } catch (error) {
    res.status(404).send({
      message: `Could not download the file. ${error}`,
    });
  }
}

async function deleteGCloud(privateUrl: string): Promise<void> {
  try {
    const { hash, bucket } = initGCloud();
    const filePath = `${hash}/${privateUrl}`;

    const file = bucket.file(filePath);
    const [fileExists] = await file.exists();

    if (fileExists) {
      await file.delete();
    }
  } catch {
    console.log(`Cannot find the file ${privateUrl}`);
  }
}

export default {
  initGCloud,
  uploadLocal,
  downloadLocal,
  deleteGCloud,
  uploadGCloud,
  downloadGCloud,
};
