// @ts-check

const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const delay = require("../../utils/delay");

/** @type {any} */
let timeout;
const UPDATE_TIMEOUT = 2000;

/**
 *
 * @param {SyncFilesFnParams} param0
 */
async function watchFiles({ files, options }) {
    try {
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            const filePath =
                typeof file == "string" ? file : file?.path ? file.path : null;
            const interval = typeof file == "object" ? file.interval : null;
            if (!filePath) continue;

            if (typeof file == "string" && !fs.existsSync(filePath)) {
                try {
                    const existingFilePath = files.find((fl) => {
                        if (typeof fl == "string") return fs.existsSync(fl);
                        if (!fl.host) return fs.existsSync(fl.path); // TODO handle remote
                    });

                    if (!existingFilePath) {
                        throw new Error("No existing Files for reference");
                    }

                    const fileDirPath =
                        typeof existingFilePath == "string"
                            ? existingFilePath
                            : existingFilePath.path;

                    if (!fs.existsSync(fileDirPath)) {
                        fs.mkdirSync(fileDirPath, { recursive: true });
                    }

                    fs.writeFileSync(filePath, "");

                    if (typeof existingFilePath == "string") {
                        sync({ filePath: existingFilePath, files, options });
                    } else {
                        sync({
                            filePath: existingFilePath.path,
                            files,
                            options,
                        });
                    }
                } catch (error) {
                    throw new Error(
                        `File Doesn't exist and couldn't be created. Please check if Directory exists.\nERROR => ${error.message}`
                    );
                }
            }
            if (typeof file == "string" && !fs.statSync(filePath).isFile()) {
                throw new Error(`'${filePath}' is not a File!`);
            }

            if (typeof file == "object" && file.host) {
                // TODO Handle SSH
            } else if (typeof file == "string") {
                sync({ options, filePath, files });

                await delay();

                fs.watchFile(
                    filePath,
                    {
                        interval: interval || 500,
                    },
                    (curr, prev) => {
                        const INTERVAL = options?.interval
                            ? options.interval
                            : UPDATE_TIMEOUT;

                        clearTimeout(timeout);

                        timeout = setTimeout(() => {
                            sync({ options, filePath, files });
                            process.exit(1);
                        }, INTERVAL);
                    }
                );
            }
        }
    } catch (error) {
        console.log("ERROR:", error.message);
        process.exit(0);
    }
}

/**
 *
 * @param {SyncFilesSyncFnParams} param0
 */
function sync({ options, filePath, files }) {
    const destFiles = files.filter((fl) => {
        if (typeof fl == "string") return fl !== filePath;
        if (fl?.path) return fl.path !== filePath;
        return false;
    });

    for (let j = 0; j < destFiles.length; j++) {
        let cmdArray = ["rsync", "-avh"];

        if (options?.delete) {
            cmdArray.push("--delete");
        }

        if (options?.exclude?.[0]) {
            options.exclude.forEach((excl) => {
                cmdArray.push(`--exclude '${excl}'`);
            });
        }

        const dstFl = destFiles[j];
        if (typeof dstFl == "string") {
            if (!fs.existsSync(dstFl)) continue;

            if (filePath === dstFl) {
                console.log(
                    `You can't sync the same paths. Please check your configuration and resolve duplicate paths`
                );
                process.exit(6);
            }

            cmdArray.push(filePath, dstFl);
            const cmd = cmdArray.join(" ");
            console.log(`Running cmd 1 => ${cmd}`);

            execSync(cmd, {
                stdio: "inherit",
            });
        } else if (dstFl.path) {
            if (!dstFl.host && !fs.existsSync(dstFl.path)) continue;

            if (filePath === dstFl.path) {
                console.log(
                    `You can't sync the same paths. Please check your configuration and resolve duplicate paths`
                );
                process.exit(6);
            }

            if (dstFl.host && dstFl.ssh_key && dstFl.user) {
                cmdArray.push("-e", `'ssh -i ${dstFl.ssh_key}'`);
                cmdArray.push(
                    filePath,
                    `${dstFl.user}@${dstFl.host}:${dstFl.path}`
                );
                const cmd = cmdArray.join(" ");
                execSync(cmd, {
                    stdio: "inherit",
                });
            } else {
                cmdArray.push(filePath, dstFl.path);

                const cmd = cmdArray.join(" ");

                console.log(`Running cmd 2 => ${cmd}`);

                execSync(cmd, {
                    stdio: "inherit",
                });
            }
        }
    }
}

module.exports = watchFiles;
