import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';

/**
 * Centralized path resolver for ConnectAI.
 *
 * Why this module exists:
 *  - Brain / agent-skills / workspace paths are read from many places (utils, sidebar,
 *    bridge, agent). Embedding the same `~`-expansion + abs-path-only check in each
 *    call site makes them drift over time.
 *  - New external integrations (skill-inject, future detached-company mode) need a
 *    single source of truth so they can't accidentally write outside the sandboxed
 *    user folders.
 *
 * Conventions:
 *  - All exported functions return absolute, normalized paths (or empty string if
 *    the user has not configured a value AND no fallback exists).
 *  - Relative-path inputs are silently rejected (returned as empty) to avoid
 *    surprising writes inside random workspaces.
 *  - This module never throws and never creates directories — callers ensure
 *    existence on their own (`fs.mkdirSync(..., { recursive: true })`).
 */

/** Expand a leading `~` / `~/` to the user's home directory. Pure function. */
export function expandTilde(raw: string): string {
    const trimmed = (raw || '').trim();
    if (!trimmed) return '';
    if (trimmed === '~') return os.homedir();
    if (trimmed.startsWith('~/')) return path.join(os.homedir(), trimmed.slice(2));
    return trimmed;
}

/**
 * Normalize a user-supplied path string. Returns an empty string for any input
 * that is empty, blank, or non-absolute after `~` expansion. Relative paths are
 * intentionally rejected — see module header.
 */
export function resolvePathInput(raw: string): string {
    const expanded = expandTilde(raw);
    if (!expanded) return '';
    if (!path.isAbsolute(expanded)) return '';
    return path.normalize(expanded);
}

/**
 * Best-effort read of a string config value. Returns empty string when VS Code
 * config is unavailable (e.g. unit tests not mocking workspace) so callers can
 * fall through to defaults without try/catch noise.
 */
function _safeGetConfigString(section: string, key: string): string {
    try {
        return vscode.workspace.getConfiguration(section).get<string>(key, '') || '';
    } catch {
        return '';
    }
}

/**
 * Active brain directory.
 *
 * Resolution order:
 *   1. VS Code config `g1nation.localBrainPath` (after `~` + abs-path normalization).
 *   2. The first configured brain profile's `localBrainPath` (handled by callers).
 *   3. Empty string — caller decides on a default (utils.ts already has the
 *      profile-aware logic; this function is only for the simple-path case).
 *
 * Note: this intentionally does NOT consult `g1nation.brainProfiles` — the
 * profile-aware resolver lives in [src/utils.ts](../utils.ts) (`_getBrainDir`)
 * and depends on the active-brain selection. Use this function only when you
 * need a plain folder path without profile semantics (e.g. external HTTP
 * endpoints injecting into the user's primary brain).
 */
export function resolveBrainDirFromConfig(): string {
    const raw = _safeGetConfigString('g1nation', 'localBrainPath');
    return resolvePathInput(raw);
}

/**
 * Resolve the agent-skills directory used by `[.agent/skills/*.md]` markdown
 * skill files (the per-workspace agent skill bank that the sidebar's
 * `_sendAgentsList` and `_createAgent` operate on).
 *
 * Resolution order:
 *   1. VS Code config `g1nation.agentSkillsPath` (after `~` + abs-path normalization),
 *      if the user explicitly pointed at a folder.
 *   2. The first VS Code workspace folder + `/.agent/skills/` (creating the
 *      folder is the caller's responsibility).
 *   3. Empty string when no workspace is open — callers must short-circuit.
 *
 * Note: a previous version hard-coded `E:\Wiki\Agent\.agent\skills` as a
 * fall-through for the original author's Windows machine. That made behavior
 * differ between machines (and never matched anything on macOS/Linux), so it
 * was removed — use `g1nation.agentSkillsPath` for a non-workspace location.
 */
export function resolveAgentSkillsDir(): string {
    const configured = resolvePathInput(_safeGetConfigString('g1nation', 'agentSkillsPath'));
    if (configured) return configured;

    const folders = vscode.workspace.workspaceFolders;
    if (folders && folders.length > 0) {
        return path.join(folders[0].uri.fsPath, '.agent', 'skills');
    }
    return '';
}

/**
 * Returns true iff `child` is the same as `parent` or a descendant of it
 * (after path normalization). Used to harden file writes against `..` traversal.
 *
 * Both paths must be absolute.
 */
export function isInside(parent: string, child: string): boolean {
    if (!parent || !child) return false;
    // Windows file systems are case-insensitive and path.resolve may emit a
    // mixed-case drive letter, so normalize case there before comparing —
    // otherwise legitimate writes get rejected just because of casing.
    const norm = (p: string) => (process.platform === 'win32' ? path.resolve(p).toLowerCase() : path.resolve(p));
    const p = norm(parent);
    const c = norm(child);
    if (c === p) return true;
    return c.startsWith(p + path.sep);
}

/**
 * Pick the best `ConfigurationTarget` to write a key to: write into whichever
 * scope already holds the value, falling back to Global.
 *
 * Why this matters: VS Code's `getConfiguration().get(key)` resolves through
 * Folder → Workspace → User → default. If a Workspace value is set and we
 * blindly write to Global, every subsequent read keeps returning the stale
 * Workspace value — which is exactly the "sidebar shows e2b but Settings
 * shows e4b" bug.
 *
 * Returns the section's effective inspect record alongside the target so
 * callers can debug or surface conflicts to the user.
 */
export function pickConfigTarget(section: string, key: string): {
    target: vscode.ConfigurationTarget;
    inspect: ReturnType<vscode.WorkspaceConfiguration['inspect']>;
} {
    const cfg = vscode.workspace.getConfiguration(section);
    const inspect = cfg.inspect(key);
    if (inspect?.workspaceFolderValue !== undefined) {
        return { target: vscode.ConfigurationTarget.WorkspaceFolder, inspect };
    }
    if (inspect?.workspaceValue !== undefined) {
        return { target: vscode.ConfigurationTarget.Workspace, inspect };
    }
    return { target: vscode.ConfigurationTarget.Global, inspect };
}
