import { exists, mkdir, readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
import * as crypto from "node:crypto";

import { ASSETS_FOLDER, MANIFEST_FOLDER, PROFILES_FOLDER } from "../consts";

import type { ManifestFile, ProfileFile } from "../commands/types";

export interface FileInfo {
  path: string;
  hash: string;
  size: number;
}

export const helpers = {
  manifest: {
    init: async () => {
      if (!(await exists(MANIFEST_FOLDER))) {
        try {
          await mkdir(MANIFEST_FOLDER, { recursive: true });
        } catch (e) {
          console.error(e);
        }
      }
    },
    isFolderExists: async () => {
      if (await exists(MANIFEST_FOLDER)) {
        return true;
      }

      await helpers.manifest.init();
      return true;
    },
    isExists: async (id: string) => {
      const manifestPath = path.join(MANIFEST_FOLDER, `${id}.json`);
      return await exists(manifestPath);
    },
    save: async (id: string, data: string, resave: boolean) => {
      if (resave) {
        await Bun.file(path.join(MANIFEST_FOLDER, `${id}.json`)).delete();
      }

      await Bun.write(path.join(MANIFEST_FOLDER, `${id}.json`), data);
    },
    generate: async (dir: string): Promise<FileInfo[]> => {
      const files: FileInfo[] = [];

      async function scanDir(currentDir: string) {
        const dirEntries = await readdir(currentDir, { withFileTypes: true });

        for (const entry of dirEntries) {
          const fullPath = path.join(currentDir, entry.name);
          const relPath = path.relative(dir, fullPath);
          if (entry.isDirectory()) {
            await scanDir(fullPath);
          } else if (entry.isFile()) {
            const fileBuffer = await readFile(fullPath);
            const hash = crypto
              .createHash("sha256")
              .update(fileBuffer)
              .digest("hex");
            const { size } = await stat(fullPath);
            files.push({ path: relPath, hash, size });
          }
        }
      }

      await scanDir(dir);
      return files;
    },
    get: async (id: string): Promise<ManifestFile[]> => {
      const isProfileEnabled = await helpers.profile.enabled(id);

      if (!isProfileEnabled) return [];
      const manifestFilePath = path.join(MANIFEST_FOLDER, `${id}.json`);

      const file = Bun.file(manifestFilePath);
      return JSON.parse(await file.text());
    },
  },
  profile: {
    save: async (name: string, data: string) => {
      if (!(await exists(PROFILES_FOLDER))) {
        await mkdir(PROFILES_FOLDER, { recursive: true });
      }

      await Bun.write(path.join(PROFILES_FOLDER, `${name}.json`), data);
    },
    getAll: async (): Promise<ProfileFile[]> => {
      const dirEntries = await readdir(PROFILES_FOLDER, {
        withFileTypes: true,
      });
      const profiles = [];

      for (const entry of dirEntries) {
        const file = Bun.file(path.join(PROFILES_FOLDER, entry.name));
        const data: ProfileFile = JSON.parse(await file.text());
        profiles.push(data);
      }

      return profiles;
    },
    enabled: async (id: string): Promise<Boolean> => {
      const profilePath = path.join(PROFILES_FOLDER, `${id}.json`);

      if (!(await exists(profilePath))) return false;

      const file = Bun.file(profilePath);
      const data: ProfileFile = JSON.parse(await file.text());

      return data.enabled;
    },
  },
  asset: {
    init: async (dir: string) => {
      const targetPath = path.join(ASSETS_FOLDER, dir);

      if (await exists(targetPath)) {
        return;
      }

      try {
        await mkdir(targetPath, { recursive: true });
      } catch (e) {
        console.error(e);
      }

      await Bun.write(
        path.join(targetPath, "your-assets-here.txt"),
        "Place your assets here",
      );
    },
  },
  system: {
    init: async () => {
      if (!(await exists(ASSETS_FOLDER))) {
        await mkdir(ASSETS_FOLDER, { recursive: true });
      }

      if (!(await exists(PROFILES_FOLDER))) {
        await mkdir(PROFILES_FOLDER, { recursive: true });
      }

      if (!(await exists(MANIFEST_FOLDER))) {
        await mkdir(MANIFEST_FOLDER, { recursive: true });
      }
    },
  },
};
