// lib/profiles/resolver.mjs
// Profile resolution layer (v4.1 SC #5-#9).
//
// Locked interface contract (per brief Preferences):
//   loadProfile(name) → ProfileObject
//     - Reads lib/profiles/<name>.yaml or custom voyage-profiles/<name>.yaml.
//     - Throws Error (cause: PROFILE_NOT_FOUND) when not found.
//     - Returns parsed object with phase_models flattened to {brief: 'sonnet', ...}
//       (object form for downstream JSON-stats; conversion from YAML list-of-dicts).
//
//   resolveProfile(argv, env) → {profile, profile_source}
//     - Resolution order: --profile flag > VOYAGE_PROFILE env > 'premium' default.
//     - profile_source: 'flag' | 'env' | 'default'.
//
//   resolveTrekcontinueProfile(planPath, argv) → {profile, profile_source}
//     - --profile flag in argv wins with 'flag'.
//     - Otherwise reads plan.md frontmatter via parseDocument; returns
//       plan-frontmatter `profile` field with 'inheritance'.
//     - If flag overrides inheritance, console.error emits an advisory.
//     - For v4.0-style plans without `profile:` field, returns 'default' premium.
//
//   validateProfileFile(path) → Result
//     - Thin wrapper around validateProfile from profile-validator.mjs.
//
// Custom.yaml lookup order: <repo-root>/voyage-profiles/<name>.yaml > ~/.claude/voyage-profiles/<name>.yaml
// Both attempted paths included in error message on miss (HIGH-risk-mitigering).

import { readFileSync, existsSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { homedir } from 'node:os';
import { parseDocument } from '../util/frontmatter.mjs';
import { validateProfile } from '../validators/profile-validator.mjs';

const __dirname = dirname(fileURLToPath(import.meta.url));
const BUILTIN_PROFILES_DIR = __dirname; // lib/profiles/
const BUILTIN_NAMES = new Set(['economy', 'balanced', 'premium']);

/**
 * Resolve the path to a profile file.
 * Built-in profiles: lib/profiles/<name>.yaml
 * Custom profiles: <repo-root>/voyage-profiles/<name>.yaml > ~/.claude/voyage-profiles/<name>.yaml
 *
 * @returns {{path: string|null, attempted: string[]}}
 */
export function findProfilePath(name, opts = {}) {
  const cwd = opts.cwd || process.cwd();
  const home = opts.home || homedir();
  const attempted = [];

  if (BUILTIN_NAMES.has(name)) {
    const builtinPath = join(BUILTIN_PROFILES_DIR, `${name}.yaml`);
    attempted.push(builtinPath);
    if (existsSync(builtinPath)) return { path: builtinPath, attempted };
  }

  // Custom: repo-root first, then home
  const repoCustom = join(cwd, 'voyage-profiles', `${name}.yaml`);
  attempted.push(repoCustom);
  if (existsSync(repoCustom)) return { path: repoCustom, attempted };

  const homeCustom = join(home, '.claude', 'voyage-profiles', `${name}.yaml`);
  attempted.push(homeCustom);
  if (existsSync(homeCustom)) return { path: homeCustom, attempted };

  return { path: null, attempted };
}

/**
 * Flatten phase_models list-of-dicts to object form: {brief: 'sonnet', research: 'opus', ...}
 */
function flattenPhaseModels(list) {
  const out = {};
  if (!Array.isArray(list)) return out;
  for (const entry of list) {
    if (entry && typeof entry === 'object' && typeof entry.phase === 'string' && typeof entry.model === 'string') {
      out[entry.phase] = entry.model;
    }
  }
  return out;
}

/**
 * Load and parse a profile file by name.
 * @param {string} name
 * @param {{cwd?: string, home?: string}} [opts]
 * @returns {{name: string, profile_version: string, phase_models: object, parallel_agents_min: number, parallel_agents_max: number, external_research_enabled: boolean, brief_reviewer_iter_cap: number, _path: string}}
 * @throws {Error} cause: PROFILE_NOT_FOUND | PROFILE_PARSE_ERROR
 */
export function loadProfile(name, opts = {}) {
  const { path, attempted } = findProfilePath(name, opts);
  if (!path) {
    const err = new Error(
      `Profile "${name}" not found. Attempted paths:\n  - ${attempted.join('\n  - ')}`,
    );
    err.cause = 'PROFILE_NOT_FOUND';
    err.attempted = attempted;
    throw err;
  }

  let text;
  try { text = readFileSync(path, 'utf-8'); }
  catch (e) {
    const err = new Error(`Cannot read profile "${name}" at ${path}: ${e.message}`);
    err.cause = 'PROFILE_READ_ERROR';
    throw err;
  }

  const doc = parseDocument(text);
  if (!doc.valid) {
    const err = new Error(`Profile "${name}" parse error: ${doc.errors[0]?.message || 'unknown'}`);
    err.cause = 'PROFILE_PARSE_ERROR';
    throw err;
  }

  const fm = doc.parsed.frontmatter || {};
  return {
    name: fm.name,
    profile_version: fm.profile_version,
    phase_models: flattenPhaseModels(fm.phase_models),
    parallel_agents_min: fm.parallel_agents_min,
    parallel_agents_max: fm.parallel_agents_max,
    external_research_enabled: fm.external_research_enabled,
    brief_reviewer_iter_cap: fm.brief_reviewer_iter_cap,
    _path: path,
  };
}

/**
 * Resolve profile name from argv + env + default.
 * Order: --profile flag > VOYAGE_PROFILE env > 'premium'.
 *
 * @param {{flags?: object} | string[]} argv  Either parsed argv or a flags-object.
 * @param {object} [env]
 * @returns {{profile: string, profile_source: 'flag'|'env'|'default'}}
 */
export function resolveProfile(argv, env = process.env) {
  const flags = (argv && typeof argv === 'object' && argv.flags) ? argv.flags : (argv || {});
  if (typeof flags['--profile'] === 'string' && flags['--profile'].length > 0) {
    return { profile: flags['--profile'], profile_source: 'flag' };
  }
  if (typeof env.VOYAGE_PROFILE === 'string' && env.VOYAGE_PROFILE.length > 0) {
    return { profile: env.VOYAGE_PROFILE, profile_source: 'env' };
  }
  return { profile: 'premium', profile_source: 'default' };
}

/**
 * Resolve profile for /trekcontinue: prefers explicit flag, falls back to plan
 * frontmatter (inheritance), then 'premium' default if plan has no profile field.
 *
 * @param {string} planPath  Path to plan.md
 * @param {{flags?: object} | string[]} argv
 * @param {{env?: object, console?: Console}} [opts]
 * @returns {{profile: string, profile_source: 'flag'|'inheritance'|'default'}}
 */
export function resolveTrekcontinueProfile(planPath, argv, opts = {}) {
  const env = opts.env || process.env;
  const con = opts.console || console;

  const flags = (argv && typeof argv === 'object' && argv.flags) ? argv.flags : (argv || {});
  const flagProfile = (typeof flags['--profile'] === 'string' && flags['--profile'].length > 0)
    ? flags['--profile'] : null;

  // Read plan-frontmatter to detect inheritance
  let planProfile = null;
  if (planPath && existsSync(planPath)) {
    try {
      const text = readFileSync(planPath, 'utf-8');
      const doc = parseDocument(text);
      if (doc.valid) {
        const fm = doc.parsed.frontmatter || {};
        if (typeof fm.profile === 'string' && fm.profile.length > 0) {
          planProfile = fm.profile;
        }
      }
    } catch {
      // swallow — degrades gracefully to default
    }
  }

  if (flagProfile) {
    if (planProfile && planProfile !== flagProfile) {
      con.error(`[voyage] profile inheritance overridden by --profile flag: ${planProfile} → ${flagProfile}`);
    }
    return { profile: flagProfile, profile_source: 'flag' };
  }

  if (planProfile) {
    return { profile: planProfile, profile_source: 'inheritance' };
  }

  // v4.0-style plan without profile: default to premium
  return { profile: 'premium', profile_source: 'default' };
}

/**
 * Validate a profile YAML file.
 * Thin wrapper for locked-interface compatibility.
 */
export function validateProfileFile(path, opts = {}) {
  return validateProfile(path, opts);
}
