import { existsSync, readFileSync, realpathSync, statSync } from "fs";
import { homedir } from "os";
import { dirname, isAbsolute, join, relative, resolve } from "path";
import { execSync } from "child_process";
import { execa } from "execa";
const STRONG_PROJECT_MARKERS = [
    ".omk/agents/root.yaml",
    ".omk/runtime-preset.json",
    ".omk/config.toml",
    ".omk/prompts/root.md",
    ".kimi/AGENTS.md",
];
const HOME_ROOT_WARNING = "effective OMK project root is HOME; set OMK_PROJECT_ROOT or OMK_DEFAULT_PROJECT_ROOT to the intended project";
const HOME_GIT_WARNING = "git root resolves to HOME and is not used as the default OMK project root";
function normalizeHome(env, explicitHome) {
    return resolve(explicitHome ?? env.OMK_ORIGINAL_HOME ?? env.HOME ?? env.USERPROFILE ?? homedir());
}
function hasControlChars(value) {
    return /[\u0000-\u001f\u007f]/.test(value);
}
function isSamePath(left, right) {
    if (!left || !right)
        return false;
    return comparablePath(left) === comparablePath(right);
}
function canonicalizePath(value) {
    const resolved = resolve(value);
    try {
        return realpathSync.native(resolved);
    }
    catch {
        return resolved;
    }
}
function comparablePath(value) {
    const canonical = canonicalizePath(value);
    return process.platform === "win32" ? canonical.toLowerCase() : canonical;
}
function projectRootRecommendation() {
    return "Run with OMK_PROJECT_ROOT=/path/to/project or set OMK_DEFAULT_PROJECT_ROOT for shells launched from HOME.";
}
function prefersCwdRoot(env) {
    const value = env.OMK_PREFER_CWD_ROOT?.trim().toLowerCase();
    return Boolean(value && value !== "0" && value !== "false" && value !== "off");
}
function respectsProjectRootEnv(env) {
    const value = env.OMK_CHAT_RESPECT_PROJECT_ROOT_ENV?.trim().toLowerCase();
    return Boolean(value && value !== "0" && value !== "false" && value !== "off");
}
function markerAt(directory) {
    for (const marker of STRONG_PROJECT_MARKERS) {
        if (existsSync(join(directory, marker)))
            return marker;
    }
    return undefined;
}
function findStrongMarkerRoot(cwd, home) {
    let current = resolve(cwd);
    const resolvedHome = resolve(home);
    while (true) {
        if (!isSamePath(current, resolvedHome)) {
            const marker = markerAt(current);
            if (marker)
                return { root: current, marker };
        }
        const parent = dirname(current);
        if (parent === current)
            return undefined;
        current = parent;
    }
}
function readDefaultProjectRootFromUserConfig(home) {
    try {
        const raw = readFileSync(join(home, ".omk", "config.toml"), "utf-8");
        let section = "";
        for (const originalLine of raw.split(/\r?\n/)) {
            const line = stripTomlComment(originalLine).trim();
            if (!line)
                continue;
            const sectionMatch = /^\[([^\]]+)]$/.exec(line);
            if (sectionMatch) {
                section = sectionMatch[1].trim();
                continue;
            }
            const kv = /^([A-Za-z0-9_.-]+)\s*=\s*(.+)$/.exec(line);
            if (!kv)
                continue;
            const key = section ? `${section}.${kv[1].trim()}` : kv[1].trim();
            if (key === "default_project_root" || key === "runtime.default_project_root") {
                return normalizeTomlValue(kv[2].trim());
            }
        }
    }
    catch {
        // Missing or unreadable user config is expected.
    }
    return undefined;
}
function stripTomlComment(line) {
    let inString = false;
    let quote = "";
    for (let i = 0; i < line.length; i += 1) {
        const char = line[i];
        if ((char === "\"" || char === "'") && line[i - 1] !== "\\") {
            if (!inString) {
                inString = true;
                quote = char;
            }
            else if (quote === char) {
                inString = false;
            }
        }
        if (char === "#" && !inString)
            return line.slice(0, i);
    }
    return line;
}
function normalizeTomlValue(value) {
    const trimmed = value.trim();
    if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
        return trimmed.slice(1, -1);
    }
    return trimmed;
}
function validateDefaultProjectRoot(value) {
    if (!value)
        return {};
    if (hasControlChars(value))
        return { error: "configured default_project_root contains control characters" };
    const resolved = resolve(value);
    try {
        const info = statSync(resolved);
        if (!info.isDirectory())
            return { error: "configured default_project_root is not a directory" };
        return { root: resolved };
    }
    catch {
        return { error: "configured default_project_root does not exist" };
    }
}
function defaultProjectRootCandidate(env, home) {
    if (env.OMK_DEFAULT_PROJECT_ROOT) {
        const checked = validateDefaultProjectRoot(env.OMK_DEFAULT_PROJECT_ROOT);
        return {
            value: checked.root,
            source: checked.root ? "default-env" : undefined,
            error: checked.error,
        };
    }
    const configured = readDefaultProjectRootFromUserConfig(home);
    const checked = validateDefaultProjectRoot(configured);
    return {
        value: checked.root,
        source: checked.root ? "default-config" : undefined,
        error: checked.error,
    };
}
function gitRootSync(cwd) {
    try {
        const gitRoot = execSync("git rev-parse --show-toplevel", {
            cwd,
            encoding: "utf-8",
            stdio: ["pipe", "pipe", "ignore"],
            timeout: 3000,
        }).trim();
        return gitRoot ? resolve(gitRoot) : undefined;
    }
    catch {
        return undefined;
    }
}
async function gitRootAsync(cwd) {
    try {
        const result = await execa("git", ["rev-parse", "--show-toplevel"], {
            cwd,
            encoding: "utf8",
            stdio: "pipe",
            timeout: 3000,
        });
        const gitRoot = result.stdout.trim();
        return gitRoot ? resolve(gitRoot) : undefined;
    }
    catch {
        return undefined;
    }
}
function createResolution(args) {
    const root = resolve(args.root);
    const home = resolve(args.home);
    const cwd = resolve(args.cwd);
    const gitRoot = args.gitRoot ? resolve(args.gitRoot) : undefined;
    return {
        root,
        source: args.source,
        cwd,
        home,
        gitRoot,
        marker: args.marker,
        configuredDefaultProjectRoot: args.defaultCandidate?.value,
        defaultProjectRootError: args.defaultCandidate?.error,
        isHomeRoot: isSamePath(root, home),
        homeIsGitRepo: isSamePath(args.gitRoot, home),
        warning: args.warning,
        recommendation: args.recommendation,
    };
}
function resolveAfterGitRoot(args) {
    const defaultCandidate = defaultProjectRootCandidate(args.env, args.home);
    if (args.marker) {
        return createResolution({
            root: args.marker.root,
            source: "strong-marker",
            cwd: args.cwd,
            home: args.home,
            gitRoot: args.gitRoot,
            marker: args.marker.marker,
            defaultCandidate,
        });
    }
    if (args.gitRoot && isSamePath(args.gitRoot, args.home) && !args.allowHomeGitRoot) {
        if (defaultCandidate.value && defaultCandidate.source) {
            return createResolution({
                root: defaultCandidate.value,
                source: defaultCandidate.source,
                cwd: args.cwd,
                home: args.home,
                gitRoot: args.gitRoot,
                defaultCandidate,
            });
        }
        const fallbackRoot = isSamePath(args.cwd, args.home) ? args.home : args.cwd;
        return createResolution({
            root: fallbackRoot,
            source: "home-git-fallback",
            cwd: args.cwd,
            home: args.home,
            gitRoot: args.gitRoot,
            defaultCandidate,
            warning: isSamePath(fallbackRoot, args.home) ? HOME_ROOT_WARNING : HOME_GIT_WARNING,
            recommendation: projectRootRecommendation(),
        });
    }
    if (args.gitRoot) {
        return createResolution({
            root: args.gitRoot,
            source: "git",
            cwd: args.cwd,
            home: args.home,
            gitRoot: args.gitRoot,
            defaultCandidate,
        });
    }
    if (isSamePath(args.cwd, args.home) && defaultCandidate.value && defaultCandidate.source) {
        return createResolution({
            root: defaultCandidate.value,
            source: defaultCandidate.source,
            cwd: args.cwd,
            home: args.home,
            defaultCandidate,
        });
    }
    return createResolution({
        root: args.cwd,
        source: "cwd",
        cwd: args.cwd,
        home: args.home,
        defaultCandidate,
        warning: isSamePath(args.cwd, args.home) ? HOME_ROOT_WARNING : undefined,
        recommendation: isSamePath(args.cwd, args.home) ? projectRootRecommendation() : undefined,
    });
}
export function resolveProjectRoot(options = {}) {
    const env = options.env ?? process.env;
    const cwd = resolve(options.cwd ?? process.cwd());
    const home = normalizeHome(env, options.home);
    const marker = findStrongMarkerRoot(cwd, home);
    if (env.OMK_PROJECT_ROOT) {
        const shouldOverrideEnvRoot = !respectsProjectRootEnv(env)
            && marker
            && !isSamePath(marker.root, env.OMK_PROJECT_ROOT)
            && (prefersCwdRoot(env)
                || (env.OMK_DEFAULT_PROJECT_ROOT !== undefined && isSamePath(env.OMK_PROJECT_ROOT, env.OMK_DEFAULT_PROJECT_ROOT)));
        if (shouldOverrideEnvRoot) {
            return createResolution({
                root: marker.root,
                source: "strong-marker",
                cwd,
                home,
                marker: marker.marker,
            });
        }
        return createResolution({
            root: env.OMK_PROJECT_ROOT,
            source: "env",
            cwd,
            home,
        });
    }
    if (prefersCwdRoot(env)) {
        return createResolution({
            root: cwd,
            source: "cwd",
            cwd,
            home,
        });
    }
    const gitRoot = gitRootSync(cwd);
    return resolveAfterGitRoot({
        cwd,
        home,
        env,
        gitRoot,
        marker,
        allowHomeGitRoot: options.allowHomeGitRoot,
    });
}
export async function resolveProjectRootAsync(options = {}) {
    const env = options.env ?? process.env;
    const cwd = resolve(options.cwd ?? process.cwd());
    const home = normalizeHome(env, options.home);
    const marker = findStrongMarkerRoot(cwd, home);
    if (env.OMK_PROJECT_ROOT) {
        const shouldOverrideEnvRoot = !respectsProjectRootEnv(env)
            && marker
            && !isSamePath(marker.root, env.OMK_PROJECT_ROOT)
            && (prefersCwdRoot(env)
                || (env.OMK_DEFAULT_PROJECT_ROOT !== undefined && isSamePath(env.OMK_PROJECT_ROOT, env.OMK_DEFAULT_PROJECT_ROOT)));
        if (shouldOverrideEnvRoot) {
            return createResolution({
                root: marker.root,
                source: "strong-marker",
                cwd,
                home,
                marker: marker.marker,
            });
        }
        return createResolution({
            root: env.OMK_PROJECT_ROOT,
            source: "env",
            cwd,
            home,
        });
    }
    if (prefersCwdRoot(env)) {
        return createResolution({
            root: cwd,
            source: "cwd",
            cwd,
            home,
        });
    }
    const gitRoot = await gitRootAsync(cwd);
    return resolveAfterGitRoot({
        cwd,
        home,
        env,
        gitRoot,
        marker,
        allowHomeGitRoot: options.allowHomeGitRoot,
    });
}
export function getProjectRoot() {
    return resolveProjectRoot().root;
}
export async function getProjectRootAsync() {
    return (await resolveProjectRootAsync()).root;
}
export function getProjectRootDiagnostics() {
    return resolveProjectRoot();
}
export function displayProjectRootPath(path, home = normalizeHome(process.env)) {
    if (!path)
        return null;
    const resolved = resolve(path);
    const resolvedHome = resolve(home);
    if (isSamePath(resolved, resolvedHome))
        return "~";
    const rel = relative(resolvedHome, resolved);
    if (rel && !rel.startsWith("..") && !isAbsolute(rel))
        return `~/${rel.replace(/\\/g, "/")}`;
    return resolved;
}
