"use strict";

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

// ─── Shared lifecycle reader (single parse per render cycle) ─────────────

/**
 * Read and parse lifecycle.json once. All other functions accept the parsed
 * data as an optional parameter to avoid redundant disk I/O. (#625)
 */
function readLifecycleData(projectDir) {
  if (!projectDir) return null;
  try {
    // Try split files first (v6+)
    const regPath = path.join(projectDir, ".claude", "lifecycle-registry.json");
    if (fs.existsSync(regPath)) {
      const registry = JSON.parse(fs.readFileSync(regPath, "utf-8"));
      // Read active_feature from state file
      let activeFeature = null;
      try {
        const statePath = path.join(
          projectDir,
          ".claude",
          "lifecycle-state.json",
        );
        if (fs.existsSync(statePath)) {
          const state = JSON.parse(fs.readFileSync(statePath, "utf-8"));
          activeFeature = state.active_feature || null;
        }
      } catch {
        /* ignore */
      }
      return {
        schema_version: registry.schema_version,
        active_feature: activeFeature,
        features: registry.features,
        project_key: registry.project_key || null,
      };
    }
    // Legacy fallback
    const lcPath = path.join(projectDir, ".claude", "lifecycle.json");
    if (!fs.existsSync(lcPath)) return null;
    return JSON.parse(fs.readFileSync(lcPath, "utf-8"));
  } catch {
    return null;
  }
}

// ─── Lifecycle ──────────────────────────────────────────────────────────────

/** Read lifecycle steps from .claude/lifecycle.json in the project dir. */
function readLifecycleSteps(projectDir, _data) {
  const data = _data || readLifecycleData(projectDir);
  if (!data || !data.active_feature || !data.features) return null;
  const feature = data.features[data.active_feature];
  if (!feature || !feature.steps) return null;
  const result = {};
  for (const step of ["spec", "design", "develop", "validate", "deploy"]) {
    result[step] = feature.steps[step] ? feature.steps[step].status : "pending";
  }
  return result;
}

/** Extract issue number from lifecycle feature slug or display name. */
function readLifecycleFeature(projectDir, _data) {
  const data = _data || readLifecycleData(projectDir);
  if (!data || !data.active_feature || !data.features) return null;
  const feature = data.features[data.active_feature];
  if (!feature) return null;
  // Prefer explicit issue_number field (set by register --issue)
  if (feature.issue_number) return `#${feature.issue_number}`;
  // Fall back: extract issue number from display_name or slug
  const name = feature.display_name || data.active_feature;
  // Match patterns like "#123", "GH-123", or bare numbers at start/after dash
  const issueMatch = name.match(/#(\d+)/);
  if (issueMatch) return `#${issueMatch[1]}`;
  // Match "123-some-feature" slug pattern
  const slugMatch = data.active_feature.match(/^(\d+)-/);
  if (slugMatch) return `#${slugMatch[1]}`;
  // Shorten the feature name for display
  const short =
    data.active_feature.length > 12
      ? data.active_feature.slice(0, 10) + ".."
      : data.active_feature;
  return short;
}

/**
 * Count parked (inactive, incomplete) features in lifecycle.json.
 * Used by compNextCmd to suggest /resume-feature when features are waiting.
 */
function readParkedCount(projectDir, _data) {
  const data = _data || readLifecycleData(projectDir);
  if (!data || !data.features) return 0;
  const STEPS = ["spec", "design", "develop", "validate", "deploy"];
  let count = 0;
  for (const [slug, feature] of Object.entries(data.features)) {
    if (feature.completed_at) continue;
    if (slug === data.active_feature) continue;
    const allDone = STEPS.every((s) => {
      const st =
        feature.steps && feature.steps[s] ? feature.steps[s].status : "pending";
      return st === "done" || st === "skipped";
    });
    if (!allDone) count++;
  }
  return count;
}

/**
 * Count features in the backlog (never started, not archived/completed/active).
 */
function readBacklogCount(projectDir, _data) {
  const data = _data || readLifecycleData(projectDir);
  if (!data || !data.features) return 0;
  let count = 0;
  for (const [slug, feature] of Object.entries(data.features)) {
    if (feature.completed_at) continue;
    if (feature.archived_at) continue;
    if (feature.first_started_at) continue;
    if (slug === data.active_feature) continue;
    count++;
  }
  return count;
}

/**
 * Find the best incomplete feature to suggest when no feature is active.
 * Returns { step, displayName } for the most recently updated parked feature
 * (previously started, not archived), or null if no incomplete features exist.
 */
function readBestIncompleteFeature(projectDir, _data) {
  const data = _data || readLifecycleData(projectDir);
  if (!data || !data.features) return null;
  const STEPS = ["spec", "design", "develop", "validate", "deploy"];
  let best = null;
  for (const [slug, feature] of Object.entries(data.features)) {
    if (feature.completed_at) continue;
    if (feature.archived_at) continue;
    if (slug === data.active_feature) continue;
    // Only consider previously-started features (parked, not backlog)
    if (!feature.first_started_at) continue;
    // Check if all steps are done/skipped — skip fully completed features
    const allDone = STEPS.every((s) => {
      const st =
        feature.steps && feature.steps[s] ? feature.steps[s].status : "pending";
      return st === "done" || st === "skipped";
    });
    if (allDone) continue;
    // Find the next pending/gated step
    let nextStep = null;
    for (const s of STEPS) {
      const st =
        feature.steps && feature.steps[s] ? feature.steps[s].status : "pending";
      if (st === "pending" || st === "gated" || st === "failed") {
        nextStep = s;
        break;
      }
    }
    if (!nextStep) continue;
    // Pick most recently updated
    const updatedAt = feature.updated_at || feature.created_at || "";
    if (!best || updatedAt > best.updatedAt) {
      best = {
        step: nextStep,
        displayName: feature.display_name || slug,
        updatedAt,
      };
    }
  }
  return best ? { step: best.step, displayName: best.displayName } : null;
}

/** Return the active feature ID ({KEY}-XXX) for display, resolving slugs if needed. */
function readLifecycleFeatureId(projectDir, _data) {
  const data = _data || readLifecycleData(projectDir);
  if (!data || !data.active_feature) return null;

  // Fast path: already {KEY}-N format. KEY may contain multiple letter
  // segments separated by '-' (e.g. ECK-DEV-12, FEAT-74, WA-1).
  if (/^[A-Z][A-Z0-9]*(?:-[A-Z][A-Z0-9]*)*-\d+$/i.test(data.active_feature)) {
    return data.active_feature;
  }

  // Slug fallback: look up feature ID in registry or features.json
  if (projectDir) {
    // Try registry first (v6+)
    try {
      const regPath = path.join(
        projectDir,
        ".claude",
        "lifecycle-registry.json",
      );
      if (fs.existsSync(regPath)) {
        const regData = JSON.parse(fs.readFileSync(regPath, "utf-8"));
        for (const [id, feat] of Object.entries(regData.features || {})) {
          if (feat.slug === data.active_feature) return id;
        }
      }
    } catch {
      /* ignore */
    }
    // Fall back to features.json
    try {
      const fpPath = path.join(projectDir, ".claude", "features.json");
      if (fs.existsSync(fpPath)) {
        const fData = JSON.parse(fs.readFileSync(fpPath, "utf-8"));
        for (const [id, feat] of Object.entries(fData.features || {})) {
          if (feat.slug === data.active_feature) return id;
        }
      }
    } catch {
      /* ignore */
    }
  }

  // Issue number fallback: derive feature ID from the feature's issue_number
  const feature = data.features ? data.features[data.active_feature] : null;
  if (feature && feature.issue_number) {
    // Read project_key from registry or features.json
    let prefix = "FEAT";
    if (data.project_key) {
      prefix = data.project_key;
    } else if (projectDir) {
      try {
        const fpPath = path.join(projectDir, ".claude", "features.json");
        if (fs.existsSync(fpPath)) {
          const fData = JSON.parse(fs.readFileSync(fpPath, "utf-8"));
          if (fData.project_key) prefix = fData.project_key;
        }
      } catch {
        /* ignore */
      }
    }
    return `${prefix}-${feature.issue_number}`;
  }

  // Last resort: return raw value
  return data.active_feature;
}

module.exports = {
  readLifecycleData,
  readLifecycleSteps,
  readLifecycleFeature,
  readLifecycleFeatureId,
  readParkedCount,
  readBacklogCount,
  readBestIncompleteFeature,
};
