// Modified from: https://raw.githubusercontent.com/epoberezkin/fast-deep-equal/master/src/index.jst
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
import * as dntShim from "../../_dnt.shims.js";
// @ts-nocheck This file is copied from a JS project, so it's not type-safe.
import { colors, log, encodeHex, SEP } from "../../deps.js";
import crypto from "node:crypto";
export function deepEqual(a, b) {
    if (a === b)
        return true;
    if (a && b && typeof a === "object" && typeof b === "object") {
        if (a.constructor !== b.constructor)
            return false;
        let length, i;
        if (Array.isArray(a)) {
            length = a.length;
            if (length != b.length)
                return false;
            for (i = length; i-- !== 0;) {
                if (!deepEqual(a[i], b[i]))
                    return false;
            }
            return true;
        }
        if (a instanceof Map && b instanceof Map) {
            if (a.size !== b.size)
                return false;
            for (i of a.entries()) {
                if (!b.has(i[0]))
                    return false;
            }
            for (i of a.entries()) {
                if (!deepEqual(i[1], b.get(i[0])))
                    return false;
            }
            return true;
        }
        if (a instanceof Set && b instanceof Set) {
            if (a.size !== b.size)
                return false;
            for (i of a.entries()) {
                if (!b.has(i[0]))
                    return false;
            }
            return true;
        }
        if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
            length = a.length;
            if (length != b.length)
                return false;
            for (i = length; i-- !== 0;) {
                if (a[i] !== b[i])
                    return false;
            }
            return true;
        }
        if (a.constructor === RegExp) {
            return a.source === b.source && a.flags === b.flags;
        }
        if (a.valueOf !== Object.prototype.valueOf) {
            return a.valueOf() === b.valueOf();
        }
        if (a.toString !== Object.prototype.toString &&
            typeof a.toString == "function") {
            return a.toString() === b.toString();
        }
        const keys = Object.keys(a);
        length = keys.length;
        if (length !== Object.keys(b).length)
            return false;
        for (i = length; i-- !== 0;) {
            if (!Object.prototype.hasOwnProperty.call(b, keys[i]))
                return false;
        }
        for (i = length; i-- !== 0;) {
            const key = keys[i];
            if (!deepEqual(a[key], b[key]))
                return false;
        }
        return true;
    }
    // true if both NaN, false otherwise
    return a !== a && b !== b;
}
export function getHeaders() {
    const headers = dntShim.Deno.env.get("HEADERS");
    if (headers) {
        const parsedHeaders = Object.fromEntries(headers.split(",").map((h) => h.split(":").map((s) => s.trim())));
        log.debug("Headers from env keys: " + JSON.stringify(Object.keys(parsedHeaders)));
        return parsedHeaders;
    }
    else {
        return undefined;
    }
}
export async function digestDir(path, conf) {
    const hashes = [];
    for await (const e of dntShim.Deno.readDir(path)) {
        const npath = path + "/" + e.name;
        if (e.isFile) {
            hashes.push(await generateHashFromBuffer(await dntShim.Deno.readFile(npath)));
        }
        else if (e.isDirectory && !e.isSymlink) {
            hashes.push(await digestDir(npath, ""));
        }
    }
    return await generateHash(hashes.join("") + conf);
}
export async function generateHash(content) {
    const messageBuffer = new TextEncoder().encode(content);
    return await generateHashFromBuffer(messageBuffer);
}
export async function generateHashFromBuffer(content) {
    const hashBuffer = await crypto.subtle.digest("SHA-256", content);
    return encodeHex(hashBuffer);
}
// export async function readInlinePath(path: string): Promise<string> {
//   return await Deno.readTextFile(path.replaceAll("/", SEP));
// }
export function readInlinePathSync(path) {
    return dntShim.Deno.readTextFileSync(path.replaceAll("/", SEP));
}
export function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}
export function isFileResource(path) {
    const splitPath = path.split(".");
    // Check for pattern: *.resource.file.* (handles both base and branch-specific)
    return (splitPath.length >= 4 &&
        splitPath[splitPath.length - 3] == "resource" &&
        splitPath[splitPath.length - 2] == "file");
}
export function printSync(input, to = dntShim.Deno.stdout) {
    let bytesWritten = 0;
    const bytes = typeof input === 'string' ? new TextEncoder().encode(input) : input;
    while (bytesWritten < bytes.length) {
        bytesWritten += to.writeSync(bytes.subarray(bytesWritten));
    }
}
// Shared repository selection logic
export async function selectRepository(repositories, operation) {
    if (repositories.length === 0) {
        throw new Error("No git-sync repositories configured in workspace");
    }
    if (repositories.length === 1) {
        const repoPath = repositories[0].git_repo_resource_path.replace(/^\$res:/, "");
        log.info(colors.cyan(`Auto-selected repository: ${colors.bold(repoPath)}`));
        return repositories[0];
    }
    // Check if we're in a non-interactive environment
    const isInteractive = dntShim.Deno.stdin.isTerminal() && dntShim.Deno.stdout.isTerminal();
    if (!isInteractive) {
        const repoPaths = repositories.map(r => r.git_repo_resource_path.replace(/^\$res:/, ""));
        throw new Error(`Multiple repositories found: ${repoPaths.join(', ')}. Use --repository to specify which one to ${operation || 'use'}.`);
    }
    // Import Select dynamically to avoid dependency issues
    const { Select } = await import("../../deps.js");
    console.log(`\nMultiple repositories found. Please select which repository to ${operation || 'use'}:\n`);
    const selectedRepo = await Select.prompt({
        message: `Select repository for ${operation || 'operation'}:`,
        options: repositories.map((repo, index) => {
            const displayPath = repo.git_repo_resource_path.replace(/^\$res:/, "");
            return {
                name: `${index + 1}. ${displayPath}`,
                value: repo.git_repo_resource_path
            };
        })
    });
    return repositories.find((r) => r.git_repo_resource_path === selectedRepo);
}
let isWin = undefined;
export async function getIsWin() {
    if (isWin === undefined) {
        const os = await import("node:os");
        isWin = os.platform() === "win32";
    }
    return isWin;
}
/**
 * Writes content to a file only if it differs from existing content.
 * Creates parent directories if they don't exist.
 *
 * @param path - The file path to write to
 * @param content - The content to write
 * @returns true if file was written, false if skipped (content unchanged)
 */
export function writeIfChanged(path, content) {
    try {
        const existing = dntShim.Deno.readTextFileSync(path);
        if (existing === content) {
            // console.log(`Content unchanged for ${path}`);
            return false; // Content unchanged, skip write
        }
    }
    catch (error) {
        // File doesn't exist or can't be read, proceed with write
        if (!(error instanceof dntShim.Deno.errors.NotFound)) {
            // If it's not a "not found" error, we might want to know about it
            // but still proceed with the write attempt
        }
    }
    // console.log(`Writing content to ${path}`);
    dntShim.Deno.writeTextFileSync(path, content);
    return true; // File was written
}
