"use strict";

const {
  USE_COLOR,
  BRAND,
  AMBER,
  SLATE,
  OK,
  WARN,
  ERR,
  MUTED,
  BOLD,
  ITALIC,
  RESET,
  lbl,
  ICONS,
} = require("./color-system");
const { formatTokens } = require("./helpers");

// ─── Components ────────────────────────────────────────────────────────────

/**
 * Each component function receives a data bag and returns
 * { vis: string, out: string } or null (to hide).
 * vis = plain text for width calculation, out = ANSI-formatted output.
 */

function compBrandVersion(d) {
  const ver = d.eckVersion || "\u2014";
  return {
    vis: `evolv Coder Kit v${ver}`,
    out: `${BRAND(`${BOLD}${ICONS.brand}evolv Coder Kit`)}${RESET} ${MUTED(`v${ver}`)}`,
  };
}

function compBrandVersionModel(d) {
  const ver = d.eckVersion || "\u2014";
  const m = d.model || "unknown";
  return {
    vis: `evolv Coder Kit v${ver} - ${m}`,
    out: `${BRAND(`${BOLD}${ICONS.brand}evolv Coder Kit`)}${RESET} ${MUTED(`v${ver}`)} ${MUTED("-")} ${SLATE(`${BOLD}${ICONS.model}${m}`)}${RESET}`,
  };
}

function compModel(d) {
  const m = d.model || "unknown";
  return {
    vis: m,
    out: SLATE(`${BOLD}${ICONS.model}${m}${RESET}`),
  };
}

function compModelLabeled(d) {
  const m = d.model || "unknown";
  return {
    vis: `Model: ${m}`,
    out: `${lbl(`${ICONS.model}Model:`)} ${SLATE(`${BOLD}${m}${RESET}`)}`,
  };
}

function compStatus(d) {
  // `statusText` was a stub that always rendered "Ready". The actual live
  // channel skills write to is `stageDescription` (via update-stage.sh →
  // bridge file). Merging them here keeps the visible label simple and frees
  // the old `Stage:` cell for other content.
  const raw = d.stageDescription || d.statusText || "Ready";
  const text = raw.length > 40 ? raw.slice(0, 37) + "..." : raw;
  const isReady = text === "Ready";

  return {
    vis: `Status:    ${text}`,
    out: isReady
      ? `${lbl(`${ICONS.status}Status:`)}    ${OK(text)}`
      : `${lbl(`${ICONS.status}Status:`)}    ${BRAND(`${BOLD}${text}${RESET}`)}`,
  };
}

function compContextBar(d) {
  const pct = d.contextPercent;
  const termW = d.termWidth || 80;
  let barCells = Math.floor(termW / 5);
  if (barCells < 9) barCells = 9;
  if (barCells > 20) barCells = 20;

  if (pct != null && !isNaN(pct)) {
    const filled = Math.floor((pct * barCells) / 100);
    const empty = barCells - filled;
    const fillChar = USE_COLOR ? "\u2588" : "#";
    const emptyChar = USE_COLOR ? "\u2591" : ".";
    const barColor = pct >= 85 ? ERR : pct >= 70 ? WARN : OK;
    const pctStr = String(pct).padStart(3) + "%";

    const fillStr = fillChar.repeat(filled);
    const emptyStr = emptyChar.repeat(empty);
    const vis = `[${fillChar.repeat(barCells)}] ${pctStr}`;
    const out = `${barColor(`[${fillStr}`)}${RESET}${MUTED(emptyStr)}${RESET}${barColor(`]`)} ${barColor(`${BOLD}${pctStr}`)}${RESET}`;
    return { vis, out };
  }

  // Stale / no data
  const emptyChar = USE_COLOR ? "\u2591" : ".";
  const staleBar = emptyChar.repeat(barCells);
  const vis = `[${staleBar}]  --%`;
  const out = USE_COLOR ? MUTED(`[${staleBar}]  --%`) : vis;
  return { vis, out };
}

function compTokensCtx(d) {
  if (d.contextWindowSize == null || d.contextPercent == null) return null;
  const used = Math.floor((d.contextPercent * d.contextWindowSize) / 100);
  const usedFmt = formatTokens(used);
  const totalFmt = formatTokens(d.contextWindowSize);
  return {
    vis: `Context: ${usedFmt} / ${totalFmt}`,
    out: `${lbl("Context:")} ${SLATE(usedFmt)} ${MUTED("/")} ${SLATE(totalFmt)}`,
  };
}

function compContextCombined(d) {
  const pct = d.contextPercent;
  const termW = d.termWidth || 80;
  let barCells = Math.floor(termW / 5);
  if (barCells < 9) barCells = 9;
  if (barCells > 20) barCells = 20;

  // Build bar + percentage
  let barPart;
  let pctStr;
  let barColor;
  if (pct != null && !isNaN(pct)) {
    const filled = Math.floor((pct * barCells) / 100);
    const empty = barCells - filled;
    const fillChar = USE_COLOR ? "\u2588" : "#";
    const emptyChar = USE_COLOR ? "\u2591" : ".";
    barColor = pct >= 85 ? ERR : pct >= 70 ? WARN : OK;
    pctStr = String(pct).padStart(3) + "%";
    const fillStr = fillChar.repeat(filled);
    const emptyStr = emptyChar.repeat(empty);
    barPart = {
      vis: `[${fillChar.repeat(barCells)}] ${pctStr}`,
      out: `${barColor(`[${fillStr}`)}${RESET}${MUTED(emptyStr)}${RESET}${barColor(`]`)} ${barColor(`${BOLD}${pctStr}`)}${RESET}`,
    };
  } else {
    const emptyChar = USE_COLOR ? "\u2591" : ".";
    const staleBar = emptyChar.repeat(barCells);
    barColor = MUTED;
    pctStr = " --%";
    barPart = {
      vis: `[${staleBar}]  --%`,
      out: MUTED(`[${staleBar}]  --%`),
    };
  }

  // Build token stats
  let statsPart = "";
  let statsVis = "";
  if (d.contextWindowSize != null && pct != null) {
    const used = Math.floor((pct * d.contextWindowSize) / 100);
    const usedFmt = formatTokens(used);
    const totalFmt = formatTokens(d.contextWindowSize);
    statsVis = ` ${usedFmt}/${totalFmt}`;
    statsPart = ` ${SLATE(usedFmt)}${MUTED("/")}${SLATE(totalFmt)}`;
  }

  return {
    vis: `Context ${barPart.vis}${statsVis}`,
    out: `${lbl("Context")} ${barPart.out}${statsPart}`,
  };
}

function compTokensIo(d) {
  const ti = d.totalInputTokens || 0;
  const to = d.totalOutputTokens || 0;
  if (ti === 0 && to === 0) return null;
  const tiFmt = formatTokens(ti);
  const toFmt = formatTokens(to);
  return {
    vis: `In: ${tiFmt} Out: ${toFmt}`,
    out: `${lbl(`${ICONS.tokens}In:`)} ${SLATE(tiFmt)} ${lbl("Out:")} ${SLATE(toFmt)}`,
  };
}

function compUsageTime(d) {
  const ms = d.durationMs || 0;
  const min = Math.floor(ms / 60000);
  if (min < 1) return null;
  let fmt;
  if (min >= 60) {
    fmt = `${Math.floor(min / 60)}h${min % 60}m`;
  } else {
    fmt = `${min}m`;
  }
  return {
    vis: `Time:      ${fmt}`,
    out: `${lbl(`${ICONS.clock}Time:`)}      ${fmt}`,
  };
}

function compDevRigor(d) {
  const rigor = d.devRigor || "standard";
  const rigorColor = rigor === "lite" ? WARN : rigor === "strict" ? OK : SLATE;
  return {
    vis: `Rigor: ${rigor}`,
    out: `${lbl(`${ICONS.mode}Rigor:`)} ${rigorColor(`${BOLD}${rigor}`)}${RESET}`,
  };
}

function compStyle(d) {
  const style = d.outputStyle || "standard";
  return {
    vis: `Style: ${style}`,
    out: `${lbl(`${ICONS.output}Style:`)} ${style}`,
  };
}

function compProtection(d) {
  const state = d.protectionState || "off";

  let protVis, protOut;
  switch (state) {
    case "on":
      protVis = "Protect: On";
      protOut = `${lbl(`${ICONS.prot}Protect:`)} ${OK(`${BOLD}On`)}${RESET}`;
      break;
    case "sudo":
      protVis = "Protect: Sudo";
      protOut = `${lbl(`${ICONS.prot}Protect:`)} ${SLATE(`${BOLD}Sudo`)}${RESET}`;
      break;
    default:
      protVis = "Protect: Off";
      protOut = `${lbl(`${ICONS.prot}Protect:`)} ${ERR(`${BOLD}Off`)}${RESET}`;
      break;
  }

  return {
    vis: protVis,
    out: protOut,
  };
}

function compProjectType(d) {
  const type = d.projectType;
  if (!type) {
    return {
      vis: "Type:  \u2014",
      out: `${lbl("Type:")}  ${MUTED("\u2014")}`,
    };
  }
  return {
    vis: `Type:  ${type}`,
    out: `${lbl("Type:")}  ${SLATE(`${BOLD}${type}`)}${RESET}`,
  };
}

function compLearning(d) {
  const level = d.learningAutonomy || "advisor";
  return {
    vis: `Learning: ${level}`,
    out: `${lbl("Learning:")} ${level}`,
  };
}

function compCost(d) {
  const cost = d.costUsd;
  if (!cost || cost <= 0) return null;
  const fmt = `$${cost.toFixed(2)}`;
  return {
    vis: `Cost: ${fmt}`,
    out: `${lbl(`${ICONS.usage}Cost:`)} ${AMBER(`${BOLD}${fmt}`)}${RESET}`,
  };
}

function compCacheHit(d) {
  const { cacheCreate, cacheRead, inputTokens } = d;
  if (!cacheCreate && !cacheRead && !inputTokens) return null;
  const total = (inputTokens || 0) + (cacheCreate || 0) + (cacheRead || 0);
  if (total <= 0) return null;
  const pct = Math.floor(((cacheRead || 0) * 100) / total);
  const cacheColor = pct >= 80 ? OK : pct >= 50 ? WARN : MUTED;
  return {
    vis: `Cache: ${pct}%`,
    out: `${lbl(`${ICONS.cache}Cache:`)} ${cacheColor(`${BOLD}${pct}%`)}${RESET}`,
  };
}

function compRateLimits(d) {
  const rl = d.rateLimits;
  if (!rl) return null;
  const fiveHour = rl.five_hour;
  const sevenDay = rl.seven_day;
  if (!fiveHour && !sevenDay) return null;

  const partsOut = [];
  const partsVis = [];

  if (fiveHour && fiveHour.used_percentage != null) {
    const pct = Math.round(fiveHour.used_percentage);
    const reset = fiveHour.resets_at
      ? new Date(fiveHour.resets_at * 1000).toLocaleTimeString([], {
          hour: "2-digit",
          minute: "2-digit",
        })
      : null;
    const color = pct < 60 ? OK : pct < 80 ? WARN : ERR;
    const body = `5h:${pct}%`;
    partsVis.push(reset ? `${body} ${reset}` : body);
    partsOut.push(
      reset ? `${color(body)} ${MUTED(reset)}` : `${color(body)}`,
    );
  }
  if (sevenDay && sevenDay.used_percentage != null) {
    const pct = Math.round(sevenDay.used_percentage);
    const reset = sevenDay.resets_at
      ? new Date(sevenDay.resets_at * 1000).toLocaleDateString([], {
          month: "short",
          day: "numeric",
        })
      : null;
    const color = pct < 60 ? OK : pct < 80 ? WARN : ERR;
    const body = `7d:${pct}%`;
    partsVis.push(reset ? `${body} ${reset}` : body);
    partsOut.push(
      reset ? `${color(body)} ${MUTED(reset)}` : `${color(body)}`,
    );
  }

  if (partsOut.length === 0) return null;
  return {
    vis: `Limits: ${partsVis.join("  ")}`,
    out: `${lbl("Limits:")} ${partsOut.join("  ")}`,
  };
}

function compLinesChanged(d) {
  const la = d.linesAdded || 0;
  const lr = d.linesRemoved || 0;
  if (la === 0 && lr === 0) return null;
  return {
    vis: `+${la}/-${lr}`,
    out: `${OK(`${BOLD}+${la}`)}${RESET}${MUTED("/")}${ERR(`${BOLD}-${lr}`)}${RESET}`,
  };
}

function compLifecycle(d) {
  if (!d.lifecycleSteps) {
    // Always show breadcrumb — all muted when no active feature
    const letters = ["S", "D", "D", "V", "D"].map((l) => MUTED(l));
    return {
      vis: "Lifecycle: S D D V D",
      out: `${lbl("Lifecycle:")} ${letters.join(" ")}`,
    };
  }

  const steps = d.lifecycleSteps;
  const STEP_KEYS = ["spec", "design", "develop", "validate", "deploy"];
  const STEP_LETTERS = {
    spec: "S",
    design: "D",
    develop: "D",
    validate: "V",
    deploy: "D",
  };
  const STRIKE = USE_COLOR ? "\x1b[9m" : "";
  const STRIKE_OFF = USE_COLOR ? "\x1b[29m" : "";
  const letters = STEP_KEYS.map((step) => {
    const status = steps[step] || "pending";
    const letter = STEP_LETTERS[step];
    switch (status) {
      case "done":
        return SLATE(`${BOLD}${letter}${RESET}`);
      case "active":
        return OK(`${BOLD}${letter}${RESET}`);
      case "gated":
        return WARN(`${BOLD}${letter}${RESET}`);
      case "failed":
        return ERR(`${BOLD}${letter}${RESET}`);
      case "skipped":
        return MUTED(`${STRIKE}${letter}${STRIKE_OFF}`);
      default:
        return MUTED(letter);
    }
  });

  // Determine active/gated/pending phase for suffix
  const PHASE_LABELS = {
    spec: "Spec",
    design: "Design",
    develop: "Develop",
    validate: "Validate",
    deploy: "Deploy",
  };
  let phaseName = null;
  for (const step of STEP_KEYS) {
    const st = steps[step] || "pending";
    if (st === "active" || st === "gated") {
      phaseName = PHASE_LABELS[step];
      break;
    }
  }
  if (!phaseName) {
    for (const step of STEP_KEYS) {
      if ((steps[step] || "pending") === "pending") {
        phaseName = PHASE_LABELS[step];
        break;
      }
    }
  }

  const suffix = phaseName ? ` (${phaseName})` : "";
  return {
    vis: `Lifecycle: S D D V D${suffix}`,
    out: `${lbl("Lifecycle:")} ${letters.join(" ")}${phaseName ? ` ${MUTED("(" + phaseName + ")")}` : ""}`,
  };
}

function compPlaceholder() {
  return { vis: "-", out: MUTED("-") };
}

function compFeature(d) {
  const conflict = d.sessionConflict && d.sessionConflict > 1 ? " (!)" : "";
  if (d.lifecycleFeatureId) {
    // Show feature ID ({KEY}-XXX) only, no slug or description
    const featureColor = conflict ? WARN : BRAND;
    return {
      vis: `Feature:   ${d.lifecycleFeatureId}${conflict}`,
      out: `${lbl("Feature:")}   ${featureColor(d.lifecycleFeatureId)}${conflict ? WARN(conflict) : ""}`,
    };
  }
  if (d.isEckProject) {
    return {
      vis: `Feature:   none`,
      out: `${lbl("Feature:")}   ${MUTED("none")}`,
    };
  }
  return null;
}

function compPhase(d) {
  if (!d.lifecycleSteps) return null;
  const STEP_KEYS = ["spec", "design", "develop", "validate", "deploy"];
  const LABELS = {
    spec: "Spec",
    design: "Design",
    develop: "Develop",
    validate: "Validate",
    deploy: "Deploy",
  };
  for (const step of STEP_KEYS) {
    if (d.lifecycleSteps[step] === "active") {
      return {
        vis: `Phase: ${LABELS[step]}`,
        out: `${MUTED("Phase:")} ${OK(LABELS[step])}`,
      };
    }
  }
  // No active step — show next pending
  for (const step of STEP_KEYS) {
    const st = d.lifecycleSteps[step] || "pending";
    if (st === "pending" || st === "gated") {
      return {
        vis: `Phase: ${LABELS[step]}`,
        out: `${MUTED("Phase:")} ${BRAND(LABELS[step])}`,
      };
    }
  }
  return null;
}

function compStage(d) {
  const desc = d.stageDescription;
  if (!desc || desc === "Ready") {
    return {
      vis: "Stage:      Ready",
      out: `${lbl("Stage:")}      ${OK("Ready")}`,
    };
  }
  const truncated = desc.length > 40 ? desc.slice(0, 37) + "..." : desc;
  return {
    vis: `Stage:      ${truncated}`,
    out: `${lbl("Stage:")}      ${OK(truncated)}`,
  };
}

function compPhaseStage(d) {
  if (!d.lifecycleSteps) return null;
  const STEP_KEYS = ["spec", "design", "develop", "validate", "deploy"];
  const LABELS = {
    spec: "Spec",
    design: "Design",
    develop: "Develop",
    validate: "Validate",
    deploy: "Deploy",
  };

  let activePhase = null;
  for (const step of STEP_KEYS) {
    if (d.lifecycleSteps[step] === "active") {
      activePhase = step;
      break;
    }
  }
  if (!activePhase) {
    for (const step of STEP_KEYS) {
      const st = d.lifecycleSteps[step] || "pending";
      if (st === "pending" || st === "gated") {
        activePhase = step;
        break;
      }
    }
  }
  if (!activePhase) return null;

  const label = LABELS[activePhase];
  const isActive = d.lifecycleSteps[activePhase] === "active";
  const stageDesc = d.stageDescription;

  // Extract fraction like "(5/9)" from stageDescription
  const fractionMatch = stageDesc && stageDesc.match(/\((\d+\/\d+)\)/);
  if (fractionMatch) {
    const combined = `${label} ${fractionMatch[1]}`;
    return {
      vis: combined,
      out: isActive ? OK(combined) : BRAND(combined),
    };
  }
  return {
    vis: label,
    out: isActive ? OK(label) : BRAND(label),
  };
}

function compNextCmd(d) {
  const STEP_KEYS = ["spec", "design", "develop", "validate", "deploy"];

  if (!d.lifecycleSteps) {
    if (!d.isEckProject) {
      return {
        vis: "Next: /eck:new-project",
        out: `${lbl("Next:")} ${AMBER("/eck:new-project")}`,
      };
    }
    if (!d.isProjectConfigured) {
      return {
        vis: "Next: /eck:start-project",
        out: `${lbl("Next:")} ${AMBER("/eck:start-project")}`,
      };
    }
    // Backlog features exist: pick next one
    if (d.backlogCount > 0) {
      return {
        vis: "Next: /eck:next-feature",
        out: `${lbl("Next:")} ${AMBER("/eck:next-feature")}`,
      };
    }
    // Nothing in queue: create a feature (green — suggested next action)
    return {
      vis: "Next: /eck:create-feature",
      out: `${lbl("Next:")} ${OK("/eck:create-feature")}`,
    };
  }

  const steps = d.lifecycleSteps;

  for (const step of STEP_KEYS) {
    if (steps[step] === "active") {
      return {
        vis: `Next: /eck:${step}`,
        out: `${lbl("Next:")} ${OK(`/eck:${step}`)}`,
      };
    }
  }

  for (const step of STEP_KEYS) {
    if (steps[step] === "failed") {
      return {
        vis: `Next: /eck:${step}`,
        out: `${lbl("Next:")} ${ERR(`/eck:${step}`)}`,
      };
    }
  }

  for (const step of STEP_KEYS) {
    const status = steps[step] || "pending";
    if (status === "pending" || status === "gated") {
      return {
        vis: `Next: /eck:${step}`,
        out: `${lbl("Next:")} ${BRAND(`/eck:${step}`)}`,
      };
    }
  }

  // All steps done — check backlog before suggesting create
  if (d.backlogCount > 0) {
    return {
      vis: "Next: /eck:next-feature",
      out: `${lbl("Next:")} ${AMBER("/eck:next-feature")}`,
    };
  }
  return {
    vis: "Next: /eck:create-feature",
    out: `${lbl("Next:")} ${SLATE("/eck:create-feature")}`,
  };
}

// Component registry
const COMPONENT_REGISTRY = {
  brand_version: compBrandVersion,
  brand_version_model: compBrandVersionModel,
  model: compModel,
  model_labeled: compModelLabeled,
  status: compStatus,
  context_bar: compContextBar,
  context_combined: compContextCombined,
  tokens_ctx: compTokensCtx,
  tokens_io: compTokensIo,
  usage_time: compUsageTime,
  dev_rigor: compDevRigor,
  style: compStyle,
  protection: compProtection,
  project_type: compProjectType,
  learning: compLearning,
  cost: compCost,
  cache_hit: compCacheHit,
  rate_limits: compRateLimits,
  lines_changed: compLinesChanged,
  lifecycle: compLifecycle,
  next_cmd: compNextCmd,
  feature: compFeature,
  phase: compPhase,
  stage: compStage,
  phase_stage: compPhaseStage,
  placeholder: compPlaceholder,
};

module.exports = {
  compBrandVersion,
  compBrandVersionModel,
  compModel,
  compModelLabeled,
  compStatus,
  compContextBar,
  compContextCombined,
  compTokensCtx,
  compTokensIo,
  compUsageTime,
  compDevRigor,
  compStyle,
  compProtection,
  compProjectType,
  compLearning,
  compCost,
  compCacheHit,
  compRateLimits,
  compLinesChanged,
  compLifecycle,
  compPlaceholder,
  compFeature,
  compPhase,
  compStage,
  compPhaseStage,
  compNextCmd,
  COMPONENT_REGISTRY,
};
