import { isWorkflowDispatchCandidateLadder } from './dispatch-matrix.js';
import { BUILTIN_EXEC_TARGETS, isWorkflowPostImplementStructuredSequence, readOatConfig, readOatLocalConfig, readUserConfig, } from './oat-config.js';
const DEFAULT_DEPENDENCIES = {
    readOatConfig,
    readOatLocalConfig,
    readUserConfig,
};
const DEFAULT_SHARED_CONFIG = {
    projects: { root: '.oat/projects/shared' },
    worktrees: { root: '.worktrees' },
    git: { defaultBranch: 'main' },
    archive: {
        s3Uri: null,
        s3SyncOnComplete: false,
        summaryExportPath: null,
        wrapUpExportPath: null,
        awsProfile: null,
        awsRegion: null,
    },
    documentation: {
        root: null,
        tooling: null,
        config: null,
        index: null,
        requireForProjectCompletion: false,
    },
    tools: {
        brainstorm: false,
        core: false,
        docs: false,
        ideas: false,
        'project-management': false,
        research: false,
        utility: false,
        workflows: false,
    },
    autoReviewAtCheckpoints: false,
};
const DEFAULT_LOCAL_CONFIG = {
    activeProject: null,
    lastPausedProject: null,
    activeIdea: null,
};
const DEFAULT_USER_CONFIG = {
    activeIdea: null,
    updateNotifications: true,
};
const DEFAULT_WORKFLOW_CONFIG = {
    workflow: {
        hillCheckpointDefault: null,
        archiveOnComplete: null,
        createPrOnComplete: null,
        postImplementSequence: null,
        reviewExecutionModel: null,
        autoReviewAtHillCheckpoints: null,
        autoNarrowReReviewScope: null,
        autoArtifactReview: {
            plan: true,
            analysis: true,
        },
        projectLog: 'auto',
        projectLogLedgerPath: '.oat/repo/reference/project-observations.md',
        designMode: null,
        dispatchCeiling: {
            preset: null,
            providers: {
                codex: null,
                claude: null,
            },
        },
        dispatchPolicy: {
            mode: null,
            policy: null,
        },
    },
};
const ENV_OVERRIDE_MAP = {
    'projects.root': 'OAT_PROJECTS_ROOT',
    'worktrees.root': 'OAT_WORKTREES_ROOT',
};
export async function resolveEffectiveConfig(repoRoot, userConfigDir, env = process.env, overrides = {}) {
    const dependencies = {
        ...DEFAULT_DEPENDENCIES,
        ...overrides,
    };
    const [shared, local, user] = await Promise.all([
        dependencies.readOatConfig(repoRoot),
        dependencies.readOatLocalConfig(repoRoot),
        dependencies.readUserConfig(userConfigDir),
    ]);
    const sharedValues = flattenConfig(shared);
    const localValues = flattenConfig(local);
    const userValues = flattenConfig(user);
    const defaultValues = {
        ...flattenConfig(DEFAULT_SHARED_CONFIG),
        ...flattenConfig(DEFAULT_LOCAL_CONFIG),
        ...flattenConfig(DEFAULT_USER_CONFIG),
        ...flattenConfig(DEFAULT_WORKFLOW_CONFIG),
    };
    const keys = new Set([
        ...Object.keys(defaultValues),
        ...Object.keys(sharedValues),
        ...Object.keys(localValues),
        ...Object.keys(userValues),
    ]);
    const resolved = {};
    for (const key of [...keys].sort()) {
        const envValue = resolveEnvOverride(key, env);
        if (envValue !== undefined) {
            resolved[key] = { value: envValue, source: 'env' };
            continue;
        }
        const localValue = localValues[key];
        if (isResolvedValue(localValue)) {
            resolved[key] = { value: localValue, source: 'local' };
            continue;
        }
        const sharedValue = sharedValues[key];
        if (isResolvedValue(sharedValue)) {
            resolved[key] = { value: sharedValue, source: 'shared' };
            continue;
        }
        const userValue = userValues[key];
        if (isResolvedValue(userValue)) {
            resolved[key] = { value: userValue, source: 'user' };
            continue;
        }
        if (key in defaultValues) {
            resolved[key] = {
                value: defaultValues[key] ?? null,
                source: 'default',
            };
        }
    }
    const hillAutoReview = resolved['workflow.autoReviewAtHillCheckpoints'];
    if (hillAutoReview?.source === 'default' &&
        shared.autoReviewAtCheckpoints !== undefined) {
        resolved['workflow.autoReviewAtHillCheckpoints'] = {
            value: shared.autoReviewAtCheckpoints,
            source: 'shared',
        };
    }
    return {
        shared,
        local,
        user,
        resolved,
    };
}
export function resolveGate(effective, skillName) {
    for (const skills of [
        effective.local.workflow?.gates?.skills,
        effective.shared.workflow?.gates?.skills,
        effective.user.workflow?.gates?.skills,
    ]) {
        if (!skills || !hasOwn(skills, skillName)) {
            continue;
        }
        return skills[skillName] ?? null;
    }
    return null;
}
export function resolveExecTargets(effective) {
    const targets = cloneExecTargetRegistry(BUILTIN_EXEC_TARGETS);
    for (const execTargets of [
        effective.user.workflow?.gates?.execTargets,
        effective.shared.workflow?.gates?.execTargets,
        effective.local.workflow?.gates?.execTargets,
    ]) {
        mergeExecTargetLayer(targets, execTargets);
    }
    return targets;
}
export function resolveExecTargetViews(effective) {
    const targets = cloneExecTargetRegistry(BUILTIN_EXEC_TARGETS);
    const views = Object.fromEntries(Object.entries(targets).map(([id, target]) => [
        id,
        {
            target: cloneExecTarget(target),
            origin: 'builtin',
            explicitlyConfigured: false,
            enabled: true,
        },
    ]));
    for (const [origin, layer] of [
        ['user', effective.user.workflow?.gates?.execTargets],
        ['shared', effective.shared.workflow?.gates?.execTargets],
        ['local', effective.local.workflow?.gates?.execTargets],
    ]) {
        if (!layer) {
            continue;
        }
        for (const [id, override] of Object.entries(layer)) {
            if (override === null) {
                const target = targets[id] ?? views[id]?.target;
                delete targets[id];
                if (target) {
                    views[id] = {
                        target: cloneExecTarget(target),
                        origin,
                        explicitlyConfigured: true,
                        enabled: false,
                    };
                }
                continue;
            }
            mergeExecTargetLayer(targets, { [id]: override });
            const target = targets[id];
            if (target) {
                views[id] = {
                    target: cloneExecTarget(target),
                    origin,
                    explicitlyConfigured: true,
                    enabled: true,
                };
            }
        }
    }
    return views;
}
function mergeExecTargetLayer(targets, layer) {
    if (!layer) {
        return;
    }
    for (const [id, override] of Object.entries(layer)) {
        if (override === null) {
            delete targets[id];
            continue;
        }
        const existing = targets[id];
        if (existing) {
            targets[id] = cloneExecTarget({
                runtime: override.runtime ?? existing.runtime,
                baseCommand: override.baseCommand ?? existing.baseCommand,
                invocation: mergeExecTargetInvocation(existing.invocation, override.invocation),
                models: override.models ?? existing.models,
                hostDetectionCommand: override.hostDetectionCommand ?? existing.hostDetectionCommand,
                availabilityCommand: override.availabilityCommand ?? existing.availabilityCommand,
                priority: override.priority ?? existing.priority,
                timeoutMs: override.timeoutMs ?? existing.timeoutMs,
            });
            continue;
        }
        const completeTarget = toCompleteExecTarget(override);
        if (completeTarget) {
            targets[id] = completeTarget;
        }
    }
}
function cloneExecTargetRegistry(registry) {
    return Object.fromEntries(Object.entries(registry).map(([id, target]) => [
        id,
        cloneExecTarget(target),
    ]));
}
function cloneExecTarget(target) {
    const next = {
        runtime: target.runtime,
        baseCommand: [...target.baseCommand],
        priority: target.priority,
    };
    if (target.hostDetectionCommand) {
        next.hostDetectionCommand = [...target.hostDetectionCommand];
    }
    if (target.invocation) {
        next.invocation = { ...target.invocation };
    }
    if (target.availabilityCommand) {
        next.availabilityCommand = [...target.availabilityCommand];
    }
    if (target.models) {
        next.models = [...target.models];
    }
    if (target.timeoutMs !== undefined) {
        next.timeoutMs = target.timeoutMs;
    }
    return next;
}
function toCompleteExecTarget(target) {
    const runtime = typeof target.runtime === 'string' ? target.runtime.trim() : '';
    if (!runtime || !isValidArgv(target.baseCommand)) {
        return null;
    }
    const completeTarget = {
        runtime,
        baseCommand: [...target.baseCommand],
        priority: typeof target.priority === 'number' && Number.isFinite(target.priority)
            ? target.priority
            : 0,
    };
    if (target.invocation) {
        completeTarget.invocation = { ...target.invocation };
    }
    if (isValidArgv(target.hostDetectionCommand)) {
        completeTarget.hostDetectionCommand = [...target.hostDetectionCommand];
    }
    if (isValidArgv(target.availabilityCommand)) {
        completeTarget.availabilityCommand = [...target.availabilityCommand];
    }
    if (isValidStringList(target.models)) {
        completeTarget.models = target.models.map((model) => model.trim());
    }
    if (target.timeoutMs !== undefined) {
        completeTarget.timeoutMs = target.timeoutMs;
    }
    return completeTarget;
}
function mergeExecTargetInvocation(existing, override) {
    if (!existing && !override) {
        return undefined;
    }
    return {
        ...existing,
        ...override,
    };
}
function isValidStringList(value) {
    return (Array.isArray(value) &&
        value.length > 0 &&
        value.every((part) => typeof part === 'string' && part.trim().length > 0));
}
function isValidArgv(value) {
    if (!Array.isArray(value) ||
        value.length === 0 ||
        !value.every((part) => typeof part === 'string')) {
        return false;
    }
    const [executable] = value;
    return executable !== undefined && executable.trim().length > 0;
}
function flattenConfig(value, prefix = '') {
    if (!isRecord(value)) {
        return {};
    }
    const flattened = {};
    for (const [key, nestedValue] of Object.entries(value)) {
        if (key === 'version') {
            continue;
        }
        const nextKey = prefix ? `${prefix}.${key}` : key;
        if (isRecord(nestedValue) && !isAtomicConfigLeaf(nextKey, nestedValue)) {
            Object.assign(flattened, flattenConfig(nestedValue, nextKey));
            continue;
        }
        flattened[nextKey] = nestedValue;
    }
    return flattened;
}
function isAtomicConfigLeaf(key, value) {
    return (isWorkflowDispatchCandidateLadder(value) ||
        (key === 'workflow.postImplementSequence' &&
            isWorkflowPostImplementStructuredSequence(value)));
}
function resolveEnvOverride(key, env) {
    const envKey = ENV_OVERRIDE_MAP[key];
    if (!envKey) {
        return undefined;
    }
    const value = env[envKey]?.trim();
    return value ? value.replace(/\/+$/, '') : undefined;
}
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function hasOwn(value, key) {
    return Object.prototype.hasOwnProperty.call(value, key);
}
function isResolvedValue(value) {
    return value !== undefined && value !== null;
}
