#!/usr/bin/env node

"use strict";

/**
 * evolv-session-guidance.js — SessionStart hook
 * Displays a contextual welcome panel based on project state.
 *
 * Variants:
 *   A) First-time user (no project-constants) — logo + getting started
 *   B) Managed project, no active feature — logo + start a feature
 *   C) Active feature with lifecycle — box with progress
 *   D) Paused session (STATE.md exists) — box with resume info
 *   E) Concise mode — single line
 */

const fs = require("fs");
const path = require("path");
const os = require("os");

// ─── Color System ───────────────────────────────────────────────────────────

const USE_COLOR = !process.env.NO_COLOR && process.env.TERM !== "dumb";

function esc(code) {
  if (!USE_COLOR) return (s) => s;
  return (s) => `\x1b[${code}m${s}\x1b[0m`;
}

const BRAND = esc("38;2;255;140;0");
const GREEN = esc("38;2;76;191;115");
const YELLOW = esc("38;2;230;190;50");
const CYAN = esc("38;2;100;200;230");
const DIM = esc("2");
const BOLD = USE_COLOR ? "\x1b[1m" : "";
const RESET = USE_COLOR ? "\x1b[0m" : "";

// ─── ASCII Logo ─────────────────────────────────────────────────────────────

const LOGO_LINES = [
  "                                ██",
  "                                ██",
  "   ▄████▄   ██    ██   ▄████▄   ██  ██    ██",
  "  ██    ██  ██    ██  ██    ██  ██  ██    ██",
  "  ███████▀  ▐██  ██▌  ██    ██  ██  ▐██  ██▌",
  "  ██         ▐█▄▄█▌   ██    ██  ██   ▐█▄▄█▌",
  "   ▀████▀     ▀██▀     ▀████▀   ██    ▀██▀",
];

// ─── Helpers ────────────────────────────────────────────────────────────────

function readFileSafe(filePath) {
  try {
    if (fs.existsSync(filePath)) return fs.readFileSync(filePath, "utf-8");
  } catch {
    /* ignore */
  }
  return null;
}

function getVersion() {
  const statePath = path.join(
    os.homedir(),
    ".claude",
    "evolv-coder-kit",
    "global-state.json",
  );
  try {
    const data = JSON.parse(readFileSafe(statePath) || "{}");
    return data.version || null;
  } catch {
    return null;
  }
}

function getProjectName(projectDir) {
  const pcPath = path.join(projectDir, ".claude", "project-constants.md");
  const content = readFileSafe(pcPath);
  if (!content) return null;
  const match = content.match(/^\|\s*PROJECT_NAME\s*\|\s*([^|]+)\s*\|/m);
  return match ? match[1].trim() : null;
}

function getOutputMode() {
  const prefsPath = path.join(
    os.homedir(),
    ".claude",
    "evolv-coder-kit",
    "user-preferences.json",
  );
  try {
    const prefs = JSON.parse(readFileSafe(prefsPath) || "{}");
    return prefs.outputMode || "standard";
  } catch {
    return "standard";
  }
}

function isFeatureComplete(f) {
  if (f.completed_at) return true;
  if (f.steps) {
    const allDone = STEPS.every(
      (s) => f.steps[s] && f.steps[s].status === "done",
    );
    if (allDone) return true;
  }
  return false;
}

function getLifecycle(projectDir) {
  // v6+: split files (lifecycle-registry.json + lifecycle-state.json)
  const regPath = path.join(projectDir, ".claude", "lifecycle-registry.json");
  const statePath = path.join(projectDir, ".claude", "lifecycle-state.json");
  let data = null;
  let isSplit = false;
  if (fs.existsSync(regPath)) {
    try {
      const reg = JSON.parse(readFileSafe(regPath) || "null");
      if (reg && reg.features) {
        let activeFeature = null;
        try {
          const state = JSON.parse(readFileSafe(statePath) || "null");
          activeFeature = state ? state.active_feature || null : null;
        } catch {
          /* ignore */
        }
        data = {
          active_feature: activeFeature,
          features: reg.features,
        };
        isSplit = true;
      }
    } catch {
      /* fall through to legacy */
    }
  }

  // Legacy v3/v5: single lifecycle.json
  const lcPath = path.join(projectDir, ".claude", "lifecycle.json");
  if (!data) {
    try {
      data = JSON.parse(readFileSafe(lcPath) || "null");
    } catch {
      return null;
    }
  }
  if (!data || !data.features) return null;

  try {
    // If there's an active feature, use it
    if (data.active_feature && data.features[data.active_feature]) {
      return {
        slug: data.active_feature,
        auto_activated: false,
        ...data.features[data.active_feature],
      };
    }

    // No active feature — try to auto-activate one
    // Skip archived and completed features
    const candidates = Object.entries(data.features).filter(
      ([, f]) => !f.archived_at && !isFeatureComplete(f),
    );
    if (candidates.length === 0) return null;

    // Priority 1: most recently worked feature (has first_started_at)
    const worked = candidates
      .filter(([, f]) => f.first_started_at)
      .sort((a, b) => {
        const aTime = a[1].updated_at || a[1].first_started_at;
        const bTime = b[1].updated_at || b[1].first_started_at;
        return new Date(bTime) - new Date(aTime);
      });

    // Priority 2: most recently created feature (newest backlog item)
    const newest = candidates.sort((a, b) => {
      const aTime = a[1].created_at || "";
      const bTime = b[1].created_at || "";
      return bTime.localeCompare(aTime);
    });

    const selected = worked.length > 0 ? worked[0] : newest[0];
    if (!selected) return null;

    const [slug, feature] = selected;
    const reason =
      worked.length > 0
        ? "Auto-activated: last worked feature"
        : "Auto-activated: newest feature";

    // Persist auto-activation
    try {
      if (isSplit) {
        // v6+: write to lifecycle-state.json only
        const stateOut = {
          schema_version: 7,
          active_feature: slug,
          updated_at: new Date().toISOString(),
        };
        const tmpPath = statePath + ".tmp";
        fs.writeFileSync(
          tmpPath,
          JSON.stringify(stateOut, null, 2) + "\n",
          "utf-8",
        );
        fs.renameSync(tmpPath, statePath);
      } else {
        data.active_feature = slug;
        const tmpPath = lcPath + ".tmp";
        fs.writeFileSync(
          tmpPath,
          JSON.stringify(data, null, 2) + "\n",
          "utf-8",
        );
        fs.renameSync(tmpPath, lcPath);
      }
    } catch {
      // Non-fatal — display the feature even if write fails
    }

    return { slug, auto_activated: true, auto_reason: reason, ...feature };
  } catch {
    return null;
  }
}

function hasPausedSession(projectDir) {
  return fs.existsSync(path.join(projectDir, ".claude", "STATE.md"));
}

function getPausedInfo(projectDir) {
  const content = readFileSafe(path.join(projectDir, ".claude", "STATE.md"));
  if (!content) return null;
  const skillMatch = content.match(/skill[:\s]+([^\n]+)/i);
  const phaseMatch = content.match(/phase[:\s]+(\d+)\s*\/?\s*(\d+)?/i);
  return {
    skill: skillMatch ? skillMatch[1].trim() : "unknown",
    phase: phaseMatch
      ? `${phaseMatch[1]}${phaseMatch[2] ? "/" + phaseMatch[2] : ""}`
      : null,
  };
}

function getLastActivity(projectDir) {
  const logPath = path.join(projectDir, ".claude", "activity.log");
  const content = readFileSafe(logPath);
  if (!content) return null;
  const lines = content.trim().split("\n").filter(Boolean);
  if (lines.length === 0) return null;
  try {
    return JSON.parse(lines[lines.length - 1]);
  } catch {
    return null;
  }
}

function timeAgo(isoString) {
  if (!isoString) return "";
  const diff = Date.now() - new Date(isoString).getTime();
  const mins = Math.floor(diff / 60000);
  if (mins < 1) return "just now";
  if (mins < 60) return `${mins}m ago`;
  const hours = Math.floor(mins / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
}

// ─── Lifecycle Bar ──────────────────────────────────────────────────────────

const STEPS = ["spec", "design", "develop", "validate", "deploy"];
const STEP_LABELS = {
  spec: "Spec",
  design: "Design",
  develop: "Dev",
  validate: "Val",
  deploy: "Deploy",
};

function renderLifecycleBar(steps) {
  return STEPS.map((step) => {
    const status = steps[step] ? steps[step].status : "pending";
    const label = STEP_LABELS[step];
    switch (status) {
      case "done":
        return GREEN(`[x] ${label}`);
      case "active":
        return YELLOW(`[>] ${label}`);
      case "failed":
        return BRAND(`[!] ${label}`);
      default:
        return DIM(`[ ] ${label}`);
    }
  }).join("  ");
}

function renderLifecycleBarCompact(steps) {
  return STEPS.map((step) => {
    const status = steps[step] ? steps[step].status : "pending";
    const label = STEP_LABELS[step];
    switch (status) {
      case "done":
        return GREEN(`[x] ${label}`);
      case "active":
        return YELLOW(`[>] ${label}`);
      case "failed":
        return BRAND(`[!] ${label}`);
      default:
        return DIM(`[ ] ${label}`);
    }
  }).join("  ");
}

// ─── Box Renderer ───────────────────────────────────────────────────────────

function box(title, rows) {
  const WIDTH = 60;
  const inner = WIDTH - 4; // accounting for "│  " and "  │"
  const lines = [];

  lines.push(
    BRAND(
      `┌ ${BOLD}${title}${RESET}${BRAND(` ${"─".repeat(Math.max(0, WIDTH - title.length - 4))}┐`)}`,
    ),
  );
  lines.push(BRAND("│") + " ".repeat(WIDTH - 2) + BRAND("│"));

  for (const [label, value] of rows) {
    const row = `  ${DIM(label.padEnd(16))}${value}`;
    // Approximate padding — actual visible width varies with ANSI
    lines.push(BRAND("│") + row + BRAND("│"));
  }

  lines.push(BRAND("│") + " ".repeat(WIDTH - 2) + BRAND("│"));
  lines.push(BRAND(`└${"─".repeat(WIDTH - 2)}┘`));

  return lines.join("\n");
}

// ─── Variant Renderers ──────────────────────────────────────────────────────

function renderLogo(version) {
  const lines = [""];
  for (const line of LOGO_LINES) {
    lines.push(BRAND(line));
  }
  lines.push("");
  lines.push(`  ${BOLD}Coder Kit${RESET}${version ? DIM(` v${version}`) : ""}`);
  return lines.join("\n");
}

function variantA(version) {
  const lines = [renderLogo(version)];
  lines.push("");
  lines.push(`  ${BOLD}Get started:${RESET}`);
  lines.push(`    ${CYAN("/eck:new-project")}        Create a new project`);
  lines.push(`    ${CYAN("/eck:migrate-project")}    Onboard existing project`);
  lines.push(`    ${CYAN("/eck:help")}               See all commands`);
  lines.push("");
  return lines.join("\n");
}

function variantB(version, projectName) {
  const lines = [
    renderLogo(version + (projectName ? ` | ${projectName}` : "")),
  ];
  lines.push("");
  lines.push(`  ${BOLD}Start a feature:${RESET}`);
  lines.push(`    ${CYAN("/eck:spec <feature>")}     Define requirements`);
  lines.push(
    `    ${CYAN("/eck:quick-task <task>")}   Skip lifecycle, do one thing`,
  );
  lines.push(`    ${CYAN("/eck:help")}               See all commands`);
  lines.push("");
  return lines.join("\n");
}

function variantC(projectName, lifecycle, lastActivity) {
  const displayName = lifecycle.display_name || lifecycle.slug;
  const lifecycleBar = renderLifecycleBar(lifecycle.steps);

  const featureLabel = lifecycle.auto_activated
    ? `${displayName}  ${DIM(`(${lifecycle.auto_reason})`)}`
    : displayName;

  const rows = [
    ["Project", projectName || "unknown"],
    ["Feature", featureLabel],
    ["Lifecycle", lifecycleBar],
  ];

  if (lastActivity) {
    const ago = timeAgo(lastActivity.ts);
    rows.push([
      "Last",
      `${CYAN(lastActivity.skill)}${ago ? ` (${lastActivity.summary || ""}, ${ago})` : ""}`,
    ]);
  }

  // Compute next step
  for (const step of STEPS) {
    const status = lifecycle.steps[step]
      ? lifecycle.steps[step].status
      : "pending";
    if (status === "pending" || status === "failed") {
      rows.push([
        "Next step",
        CYAN(`/eck:${step} ${lifecycle.display_name || lifecycle.slug}`),
      ]);
      break;
    }
  }

  return "\n" + box("evolv Coder Kit", rows) + "\n";
}

function variantD(projectName, lifecycle, pausedInfo) {
  const displayName = lifecycle.display_name || lifecycle.slug;
  const lifecycleBar = renderLifecycleBar(lifecycle.steps);

  const rows = [
    ["Project", projectName || "unknown"],
    ["Feature", displayName],
    ["Lifecycle", lifecycleBar],
  ];

  const pauseDesc =
    pausedInfo.skill + (pausedInfo.phase ? ` Phase ${pausedInfo.phase}` : "");
  rows.push(["Paused at", pauseDesc]);
  rows.push(["Resume with", CYAN("/eck:resume")]);

  return "\n" + box("evolv Coder Kit", rows) + "\n";
}

function variantE(projectName, lifecycle) {
  const bar = renderLifecycleBarCompact(lifecycle.steps);
  let nextCmd = "";
  for (const step of STEPS) {
    const status = lifecycle.steps[step]
      ? lifecycle.steps[step].status
      : "pending";
    if (status === "pending" || status === "failed") {
      nextCmd = `/eck:${step}`;
      break;
    }
  }
  return `${projectName || "project"} | ${bar}${nextCmd ? ` | Next: ${CYAN(nextCmd)}` : ""}`;
}

// ─── Main ───────────────────────────────────────────────────────────────────

function main() {
  try {
    const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
    const version = getVersion();
    const projectName = getProjectName(projectDir);
    const outputMode = getOutputMode();
    const lifecycle = getLifecycle(projectDir);
    const isPaused = hasPausedSession(projectDir);

    // Concise mode — single line
    if (outputMode === "concise" && lifecycle) {
      console.log(variantE(projectName, lifecycle));
      process.exit(0);
    }

    // Variant D: Paused session
    if (isPaused && lifecycle) {
      const pausedInfo = getPausedInfo(projectDir);
      console.log(
        variantD(
          projectName,
          lifecycle,
          pausedInfo || { skill: "unknown", phase: null },
        ),
      );
      process.exit(0);
    }

    // Variant C: Active lifecycle
    if (lifecycle) {
      const lastActivity = getLastActivity(projectDir);
      console.log(variantC(projectName, lifecycle, lastActivity));
      process.exit(0);
    }

    // Variant B: Managed project, no feature
    // Check for project-catalog.yaml to distinguish managed from unmanaged
    const catalogPath = path.join(
      projectDir,
      ".claude",
      "project-catalog.yaml",
    );
    if (projectName && fs.existsSync(catalogPath)) {
      console.log(variantB(version, projectName));
      process.exit(0);
    }

    // Unmanaged project with project-constants but no catalog
    if (projectName) {
      const lines = [renderLogo(version)];
      lines.push("");
      lines.push(`  ${BOLD}Unmanaged project:${RESET} ${projectName}`);
      lines.push(`    ${CYAN("/eck:migrate-project")}    Onboard this project`);
      lines.push(
        `    ${CYAN("/eck:audit-unmanaged-project")}  Audit infrastructure`,
      );
      lines.push(`    ${CYAN("/eck:help")}               See all commands`);
      lines.push("");
      console.log(lines.join("\n"));
      process.exit(0);
    }

    // Variant A: First-time user (no project-constants at all)
    console.log(variantA(version));
    process.exit(0);
  } catch (err) {
    // Non-blocking — log error for debugging, show fallback
    if (process.env.EVOLV_DEBUG) {
      console.error(`[session-guidance] ${err.message}`);
    }
    console.log(variantA(null));
    process.exit(0);
  }
}

module.exports = {
  getProjectName,
  getLifecycle,
  isFeatureComplete,
  renderLifecycleBar,
  STEPS,
  STEP_LABELS,
};

if (require.main === module) {
  main();
}
