import type { StorageOptions } from "@google-cloud/storage";
import { Storage } from "@google-cloud/storage";
import type { ReadStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { env } from "../env";

export interface File {
  name: string;
  mime: string;
  size: number;
  stream?: ReadStream;
  buffer?: Buffer;
}

interface Config {
  bucket: string;
  baseURL?: string;
  options: StorageOptions;
}

const init = () => {
  const { handleUpload, handleUploadStream, handleDelete, handleGenerateURL } =
    gcsStorage();
  return {
    isPrivate() {
      return false;
    },
    getSignedUrl(file: File) {
      return handleGenerateURL(file);
    },
    upload(file: File) {
      return handleUpload(file);
    },
    uploadStream(file: File) {
      return handleUploadStream(file);
    },
    delete(file: File) {
      return handleDelete(file);
    },
  };
};

function getUploadHandler(bucket: string, _baseURL: string, client: Storage) {
  return async (file: File) => {
    try {
      const gcsFile = client.bucket(bucket).file(file.name);
      await gcsFile.save(file.buffer!, {
        metadata: {
          contentType: file.mime,
          cacheControl: "public, max-age=33650",
        },
      });
    } catch (error) {
      console.error(`Error uploading file to Google Cloud Storage`);
      throw error;
    }
  };
}
function getUploadStreamHandler(
  bucket: string,
  _baseURL: string,
  client: Storage,
) {
  return async (file: File) => {
    try {
      const gcsFile = client.bucket(bucket).file(file.name);
      await pipeline(
        file.stream!,
        gcsFile.createWriteStream({
          metadata: {
            contentType: file.mime,
            cacheControl: "public, max-age=33650",
          },
        }),
      );
    } catch (error) {
      console.error(`Error uploading file to Google Cloud Storage`);
      throw error;
    }
  };
}
function getDeleteHandler(bucket: string, client: Storage) {
  return async (file: File) => {
    try {
      await client
        .bucket(bucket)
        .file(file.name)
        .delete({ ignoreNotFound: true });
    } catch (error) {
      console.log(error);
      throw new Error("Failed to delete");
    }
  };
}

function generateURL(bucket: string, client: Storage) {
  return (file: File) =>
    decodeURIComponent(client.bucket(bucket).file(file.name).publicUrl());
}

function gcsStorage() {
  const baseConfig: Config = {
    bucket: env.GetString("GOOGLE_CLOUD_STORAGE_BUCKET_NAME"),
    options: {
      credentials: JSON.parse(
        env.GetString("GOOGLE_SERVICE_ACCOUNT_CREDENTIALS"),
      ) as Record<string, string>,
      projectId: env.GetString("GOOGLE_CLOUD_PROJECT_ID"),
    },
  };
  const gcsClient = new Storage(baseConfig.options);
  return {
    handleUpload: getUploadHandler(
      baseConfig.bucket!,
      baseConfig.baseURL!,
      gcsClient,
    ),
    handleUploadStream: getUploadStreamHandler(
      baseConfig.bucket!,
      baseConfig.baseURL!,
      gcsClient,
    ),
    handleDelete: getDeleteHandler(baseConfig.bucket!, gcsClient),
    handleGenerateURL: generateURL(baseConfig.bucket!, gcsClient),
  };
}

export { init };
