/**
 * Trigger processing for repo-based AILF tasks.
 *
 * Reads .ailf/config.yaml, determines the current execution context
 * (PR, main merge, schedule, manual), and resolves which trigger
 * config applies. This drives whether the pipeline runs in validate-only
 * mode or full eval mode, and whether results are blocking.
 *
 * @see docs/archive/exec-plans/tasks-as-content/phase-4-repo-based-tasks.md
 */
import { existsSync, readFileSync } from "fs";
import { resolve } from "path";
import { load } from "js-yaml";
import { parseRepoConfig } from "./repo-schemas.js";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Load and resolve the trigger configuration for the current context.
 *
 * @param repoRoot - Root of the external repo (contains .ailf/)
 * @param context - The current execution context (PR, main, schedule, manual)
 * @returns The resolved trigger config, or a default (eval, non-blocking)
 */
export function resolveTrigger(repoRoot, context) {
    const configPath = resolve(repoRoot, ".ailf", "config.yaml");
    if (!existsSync(configPath)) {
        // No config file — use sensible defaults
        return defaultTrigger(context);
    }
    const raw = readFileSync(configPath, "utf-8");
    const parsed = load(raw);
    const config = parseRepoConfig(parsed);
    return matchTrigger(config, context);
}
/**
 * Detect the current trigger context from environment variables.
 *
 * Reads GitHub Actions env vars to determine if we're in a PR,
 * on the main branch, running on schedule, or manual dispatch.
 */
export function detectTriggerContext() {
    const eventName = process.env.GITHUB_EVENT_NAME;
    const ref = process.env.GITHUB_REF ?? "";
    if (eventName === "schedule") {
        return { type: "schedule" };
    }
    if (eventName === "workflow_dispatch") {
        return { type: "manual" };
    }
    if (eventName === "pull_request" || eventName === "pull_request_target") {
        const branch = process.env.GITHUB_HEAD_REF ?? ref.replace("refs/heads/", "");
        // Changed files could come from GITHUB_EVENT_PATH or git diff
        // For now, return without changedFiles — the caller can populate from git
        return { type: "pr", branch };
    }
    // Default: check if we're on main/master
    if (ref === "refs/heads/main" || ref === "refs/heads/master") {
        return { type: "main" };
    }
    return { type: "manual" };
}
// ---------------------------------------------------------------------------
// Internal matching
// ---------------------------------------------------------------------------
function matchTrigger(config, context) {
    const triggers = config.triggers;
    if (!triggers)
        return defaultTrigger(context);
    switch (context.type) {
        case "pr": {
            // Check pr-task-change first (more specific), then pr (general)
            const prTaskChange = triggers["pr-task-change"];
            if (prTaskChange &&
                hasPathMatch(prTaskChange.paths, context.changedFiles)) {
                return {
                    matched: true,
                    mode: prTaskChange.mode ?? "eval",
                    blocking: prTaskChange.blocking ?? false,
                    notify: prTaskChange.notify ?? true,
                    compare: prTaskChange.compare ?? false,
                    paths: prTaskChange.paths,
                };
            }
            const pr = triggers.pr;
            if (pr) {
                return {
                    matched: true,
                    mode: pr.mode ?? "validate-only",
                    blocking: pr.blocking ?? false,
                    notify: pr.notify ?? true,
                    compare: pr.compare ?? false,
                    paths: pr.paths,
                };
            }
            return defaultTrigger(context);
        }
        case "main": {
            const main = triggers.main;
            if (main) {
                return {
                    matched: true,
                    mode: main.mode ?? "eval",
                    blocking: main.blocking ?? false,
                    notify: main.notify ?? true,
                    compare: main.compare ?? false,
                };
            }
            return defaultTrigger(context);
        }
        case "schedule": {
            const schedule = triggers.schedule;
            if (schedule) {
                return {
                    matched: true,
                    mode: schedule.mode ?? "eval",
                    blocking: false, // schedule is never blocking
                    notify: schedule.notify ?? true,
                    compare: schedule.compare ?? true,
                    cron: schedule.cron,
                };
            }
            return defaultTrigger(context);
        }
        case "manual":
            return defaultTrigger(context);
    }
}
/**
 * Check if any changed files match the trigger's path patterns.
 * Uses simple glob-like prefix matching (not full glob).
 * Returns true if no paths are specified (match-all).
 */
function hasPathMatch(patterns, changedFiles) {
    if (!patterns || patterns.length === 0)
        return true;
    if (!changedFiles || changedFiles.length === 0)
        return false;
    return changedFiles.some((file) => patterns.some((pattern) => {
        // Simple glob: ".ailf/**" matches any path starting with ".ailf/"
        const prefix = pattern.replace(/\*\*$/, "").replace(/\*$/, "");
        return file.startsWith(prefix);
    }));
}
function defaultTrigger(context) {
    return {
        matched: false,
        mode: context.type === "pr" ? "validate-only" : "eval",
        blocking: false,
        notify: context.type !== "manual",
        compare: context.type === "schedule",
    };
}
