import AdmZip from "adm-zip";
import path from "path";
import fs from "fs";
import { execSync } from "child_process";
import { createLog } from "../services/logger/logger.js";
import { getAppInfo } from "../globalUtils/appInfo.js";

// create the ignore list
const ignoreList = [
    ".git",
    "builds",
    "server",
    "node_modules",
    "apiDocsLSTV2",
    "testFiles",
    ".env",
    ".gitignore",
    ".versionrc.json",
    "drizzle-dev.config.ts",
    "nssm.exe",
    "postgresql-17.2-3-windows-x64.exe",
    // front end ignore
    "frontend/node_modules",
    "fonrtend/.env",
    "frontend/public",
    "frontend/src",
    "frontend/.gitignore",
    "frontend/eslint.config.js",
    "frontend/index.html",
    "frontend/package.json",
    "frontend/package-lock.json",
    "frontend/README.md",
    "frontend/tsconfig.json",
    "frontend/tsconfig.app.json",
    "frontend/tsconfig.node.json",
    "frontend/vite.config.ts",
    "frontend/components.json",
    //misc files
    "jsTesting",
    "dotnetwrapper",
    "prodBuild",
];

const shouldIgnore = (itemPath: any) => {
    const normalizedItemPath = itemPath.replace(/\\/g, "/");

    return ignoreList.some((ignorePattern) => {
        const normalizedIgnorePatther = ignorePattern.replace(/\\/g, "/");
        return (
            normalizedItemPath === normalizedIgnorePatther ||
            normalizedItemPath.startsWith(`${normalizedIgnorePatther}/`)
        );
    });
};

const addToZip = (zip: any, currentPath: string, rootPath: string) => {
    const items = fs.readdirSync(currentPath);

    items.forEach((item) => {
        const itemPath = path.join(currentPath, item);
        const relativePath = path.relative(rootPath, itemPath);

        // Skip if the item is in the ignore list
        if (shouldIgnore(relativePath)) {
            createLog("info", "lst", "zipUpBuild", `Ignoring: ${relativePath}`);
            return;
        }

        const stat = fs.statSync(itemPath);

        if (stat.isDirectory()) {
            // If it's a directory, recursively add its contents
            addToZip(zip, itemPath, rootPath);
        } else {
            // If it's a file, add it to the zip with the preserved folder structure
            zip.addLocalFile(itemPath, path.dirname(relativePath));
        }
    });
};

const updateBuildNumber = (appLock: string) => {
    const packagePath = path.join(appLock, "package.json"); // Adjust path if necessary

    try {
        // Read package.json
        const pkgData = fs.readFileSync(packagePath, "utf8");
        const pkgJson = JSON.parse(pkgData);

        // Ensure admConfig exists
        if (pkgJson.admConfig && typeof pkgJson.admConfig.build === "number") {
            // Increment the build number
            pkgJson.admConfig.build += 1;

            // Write the updated data back
            fs.writeFileSync(
                packagePath,
                JSON.stringify(pkgJson, null, 2),
                "utf8"
            );

            createLog(
                "info",
                "lst",
                "zipUpBuild",
                `Build number updated to: ${pkgJson.admConfig.build}`
            );
            // Auto-commit changes
            execSync("git add package.json");
            execSync(
                `git commit -m "ci(release): bump build number to ${pkgJson.admConfig.build}"`
            );
        } else {
            createLog(
                "error",
                "lst",
                "zipUpBuild",
                "admConfig.build is missing or not a number"
            );
        }
    } catch (error) {
        createLog(
            "error",
            "lst",
            "zipUpBuild",
            `Error updating build number: ${error}`
        );
    }
};

export const createZip = async (appLock: string) => {
    const app = await getAppInfo(appLock);
    const zip = new AdmZip();

    //dest path for this app... hard coded for meow will be in db later
    const destPath = `${process.env.DEVFOLDER}\\builds`;
    const srcPath = `${process.env.DEVFOLDER}`;

    addToZip(zip, srcPath, srcPath);

    // Write the zip file to disk
    const outputZipPath = path.join(
        destPath,
        `${app.name}-${app.version}-${app.admConfig.build}.zip`
    );
    zip.writeZip(outputZipPath);

    createLog(
        "info",
        "lst",
        "zipUpBuild",
        `Zip file created at ${outputZipPath}`
    );
    updateBuildNumber(appLock);

    // only keep the last 5 builds for the type we have.
    try {
        const appFiles = fs
            .readdirSync(destPath)
            .filter((file) => file.startsWith(app.name)) // Ensure only backend files are matched
            .map((file) => ({
                name: file,
                time: fs.statSync(path.join(destPath, file)).mtime.getTime(),
            }))
            .sort((a, b) => a.time - b.time); // Sort by modification time (oldest first)

        createLog(
            "info",
            "lst",
            "zipUpBuild",
            `app Files (sorted by time):", ${JSON.stringify(appFiles)}`
        );

        if (appFiles.length > 20) {
            appFiles.slice(0, -20).forEach((file) => {
                const filePath = path.join(destPath, file.name);
                try {
                    fs.unlinkSync(filePath);
                    createLog(
                        "info",
                        "lst",
                        "zipUpBuild",
                        `Deleted: ${file.name}`
                    );
                } catch (error: any) {
                    createLog(
                        "error",
                        "lst",
                        "zipUpBuild",
                        `Failed to delete ${file.name}: ${error.message}`
                    );
                }
            });
        } else {
            createLog("info", "lst", "zipUpBuild", "No files to delete.");
        }
    } catch (error: any) {
        createLog(
            "error",
            "lst",
            "zipUpBuild",
            `Error reading directory or deleting files:", ${error.message}`
        );
    }
};

//createZip("C:\\Users\\matthes01\\Documents\\lstv2");

// Only call `createZip` if the script is executed directly

if (process.argv.length > 2) {
    const location = process.argv[2];

    if (!location) {
        createLog("error", "lst", "zipUpBuild", "Error: No location provided.");
        process.exit(1);
    } else {
        createLog("info", "lst", "zipUpBuild", "Startiing the zip process.");
    }

    createZip(location);
} else {
    createLog("error", "lst", "zipUpBuild", "Error: No location provided.");
}
