import process from 'node:process';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Box, Text, render, useApp, useInput, useStdout } from 'ink';
import { selectDiscoveredSite } from './environment.mjs';
import { formatStatusReport, formatStatusSummary } from './format.mjs';
import { runRepairRelease, runReset, runSupportBundle } from './lifecycle.mjs';
import { collectStatus } from './status.mjs';
import { listAppliancePods, readPodLogsSince, readPodLogsTail } from './workloads.mjs';

const ACTION_GROUPS = [
  {
    title: 'Operations',
    actions: ['Status', 'Workloads'],
  },
  {
    title: 'System',
    actions: ['Repair Release', 'Support Bundle', 'Reset'],
  },
];
const ACTIONS = ACTION_GROUPS.flatMap((group) => group.actions);

const YES_NO_OPTIONS = ['yes', 'no'];
const BRAND_PRIMARY = 'magentaBright';
const BRAND_SECONDARY = 'cyanBright';
const TEXT_MUTED = 'white';
const COLOR_OK = 'greenBright';
const COLOR_WARN = 'yellowBright';
const COLOR_ERROR = 'redBright';
const WORKLOAD_REFRESH_MS = 5000;
const LOG_POLL_MS = 1500;
const LOG_VIEW_HEIGHT = 16;
const LOG_CHUNK_LINES = 120;
const LOG_MAX_LINES = 400;

function clampIndex(value, length) {
  if (length <= 0) {
    return 0;
  }
  if (value < 0) {
    return length - 1;
  }
  if (value >= length) {
    return 0;
  }
  return value;
}

function cycleOption(options, current, delta) {
  if (!options.length) {
    return current;
  }
  const index = options.indexOf(current);
  const start = index >= 0 ? index : 0;
  const next = (start + delta + options.length) % options.length;
  return options[next];
}

function lineColor(line = '') {
  const normalized = String(line).toLowerCase();

  if (
    normalized.includes('error') ||
    normalized.includes('failed') ||
    normalized.includes('unhealthy') ||
    normalized.includes('blocked') ||
    normalized.includes('timed out') ||
    normalized.includes('crashloop') ||
    normalized.includes('imagepullbackoff') ||
    normalized.includes('not ready')
  ) {
    return COLOR_ERROR;
  }

  if (
    normalized.includes('warning') ||
    normalized.includes('pending') ||
    normalized.includes('reconciling') ||
    normalized.includes('installing') ||
    normalized.includes('unknown') ||
    normalized.includes('unavailable')
  ) {
    return COLOR_WARN;
  }

  if (
    normalized.includes('healthy') ||
    normalized.includes('ready') ||
    normalized.includes('api reachable: true') ||
    normalized.includes('no blocker detected') ||
    normalized.includes('completed successfully') ||
    normalized.includes('done:')
  ) {
    return COLOR_OK;
  }

  return undefined;
}

function splitLabelValue(line = '') {
  const text = String(line);
  const separatorIndex = text.indexOf(': ');
  if (separatorIndex <= 0) {
    return null;
  }

  return {
    label: text.slice(0, separatorIndex + 1),
    value: text.slice(separatorIndex + 2),
  };
}

function truncateText(value, width) {
  const text = String(value ?? '');
  if (text.length <= width) {
    return text.padEnd(width);
  }
  if (width <= 1) {
    return text.slice(0, width);
  }
  return `${text.slice(0, width - 1)}…`;
}

function alignRight(value, width) {
  return String(value ?? '').padStart(width);
}

const WORKLOAD_COLUMNS = {
  pod: 31,
  namespace: 12,
  status: 18,
  ready: 7,
  restarts: 8,
  age: 5,
};

function renderLines(lines, keyPrefix, options = {}) {
  const { fallbackColor, bold = false } = options;
  return lines.map((line, index) =>
    {
      const text = line || ' ';
      const parts = splitLabelValue(text);
      const valueColor = lineColor(text) || fallbackColor;

      if (!parts) {
        return React.createElement(
          Text,
          {
            key: `${keyPrefix}-${index}`,
            color: valueColor,
            bold: bold && index === 0,
          },
          text,
        );
      }

      return React.createElement(
        Text,
        {
          key: `${keyPrefix}-${index}`,
          bold: bold && index === 0,
        },
        React.createElement(Text, { color: BRAND_SECONDARY }, `${parts.label} `),
        React.createElement(Text, { color: valueColor }, parts.value || ''),
      );
    },
  );
}

function mapProgressEvent(event) {
  if (event.type === 'phase') {
    return `[${event.phase}] ${event.line || ''}`.trim();
  }
  if (event.type === 'error') {
    return `ERROR: ${event.line}`;
  }
  if (event.type === 'done') {
    return `DONE: ${event.line}`;
  }
  return event.line || '';
}

function makeResetDefaults(env) {
  return {
    challenge: '',
    expected: `WIPE ${env.site?.siteId || env.suggestedSiteId || 'appliance-single-node'}`,
  };
}

function makeRepairReleaseDefaults() {
  return {
    releaseName: 'alga-core',
    cleanupWorkloads: 'yes',
  };
}

function makeSupportBundleDefaults() {
  return {
    outputDir: process.cwd(),
  };
}

function summarizeStatus(status) {
  if (!status) {
    return ['Status unavailable'];
  }
  return formatStatusSummary(status);
}

function shortStatusReport(status) {
  if (!status) {
    return ['Unable to collect status'];
  }
  const report = formatStatusReport(status);
  return [
    ...report.summary,
    '',
    'Talos:',
    ...report.host,
    '',
    'Kubernetes:',
    ...report.cluster,
    '',
    'Flux:',
    ...report.flux,
    '',
    'Workloads:',
    ...report.workloads,
  ];
}

function trimLogLines(lines, maxLines = LOG_MAX_LINES) {
  if (lines.length <= maxLines) {
    return lines;
  }
  return lines.slice(lines.length - maxLines);
}

function applyLogTail(nextLines, previous = []) {
  const merged = [];
  for (const line of nextLines) {
    merged.push({
      id: `${line.timestamp || 'no-ts'}|${line.text}`,
      timestamp: line.timestamp,
      text: line.text,
    });
  }

  if (!merged.length) {
    return trimLogLines(previous);
  }

  return trimLogLines(merged);
}

function appendLiveLogLines(previous, incoming) {
  if (!incoming.length) {
    return previous;
  }

  const knownIds = new Set(previous.map((line) => line.id));
  const next = [...previous];
  for (const line of incoming) {
    const row = {
      id: `${line.timestamp || 'no-ts'}|${line.text}`,
      timestamp: line.timestamp,
      text: line.text,
    };
    if (!knownIds.has(row.id)) {
      knownIds.add(row.id);
      next.push(row);
    }
  }
  return trimLogLines(next);
}

function prependOlderLines(previous, expandedTail) {
  if (!expandedTail.length) {
    return previous;
  }
  const normalized = expandedTail.map((line) => ({
    id: `${line.timestamp || 'no-ts'}|${line.text}`,
    timestamp: line.timestamp,
    text: line.text,
  }));
  const existing = new Set(previous.map((line) => line.id));
  const older = normalized.filter((line) => !existing.has(line.id));
  if (!older.length) {
    return previous;
  }
  return trimLogLines([...older, ...previous]);
}

function FieldList({ fields, values, selectedIndex }) {
  return React.createElement(
    Box,
    { flexDirection: 'column' },
    ...fields.map((field, index) => {
      const selected = index === selectedIndex;
      const pointer = selected ? '>' : ' ';
      const rawValue = values[field.key] ?? '';
      const suffix = field.type === 'secret' ? String(rawValue).replace(/./g, '*') : rawValue;
      return React.createElement(
        Text,
        { key: field.key, bold: selected },
        React.createElement(Text, { color: selected ? BRAND_SECONDARY : BRAND_PRIMARY }, `${pointer} ${field.label}: `),
        React.createElement(Text, { color: selected ? undefined : TEXT_MUTED }, suffix),
      );
    }),
  );
}

function Header({ env, status }) {
  const lines = summarizeStatus(status);
  return React.createElement(
    Box,
    { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column' },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Alga PSA Operator'),
    React.createElement(
      Text,
      { color: BRAND_SECONDARY },
      `Site: ${env.site?.siteId || 'unselected'}  Node IP: ${env.nodeIp || 'unknown'}`,
    ),
    React.createElement(
      Text,
      { color: TEXT_MUTED },
      `Release: ${status?.release?.selectedReleaseVersion || 'unknown'}`,
    ),
    ...renderLines(lines.slice(0, 2), 'header-summary', { fallbackColor: TEXT_MUTED }),
  );
}

function ActionNav({ selectedIndex, compactLayout }) {
  return React.createElement(
    Box,
    {
      borderStyle: 'round',
      borderColor: BRAND_SECONDARY,
      paddingX: 1,
      flexDirection: 'column',
      width: compactLayout ? undefined : 26,
      minWidth: compactLayout ? undefined : 26,
      flexShrink: 0,
    },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Actions'),
    ...ACTION_GROUPS.flatMap((group) => {
      const section = [
        React.createElement(Text, { key: `${group.title}-title`, color: BRAND_SECONDARY, bold: true }, group.title),
      ];

      for (const label of group.actions) {
        const index = ACTIONS.indexOf(label);
        const selected = index === selectedIndex;
        const color = label === 'Reset'
          ? (selected ? COLOR_ERROR : COLOR_WARN)
          : (selected ? BRAND_SECONDARY : TEXT_MUTED);

        section.push(
          React.createElement(
            Text,
            {
              key: label,
              color,
              bold: selected,
            },
            `${selected ? '>' : ' '} ${label}`,
          ),
        );
      }

      return section;
    }),
  );
}

function HelpStrip({ view, busy, formName }) {
  let text = '↑/↓ move  Enter select  q quit';
  if (view === 'site-select') {
    text = '↑/↓ select site  Enter confirm  q quit';
  } else if (view === 'form') {
    text = `${formName}: ↑/↓ field  ←/→ option  type edit  Backspace delete  Enter confirm  Esc back`;
  } else if (view === 'confirm') {
    text = `${formName}: Enter run  Esc cancel`;
  } else if (view === 'workloads') {
    text = 'Workloads: ↑/↓ select pod  Enter logs  r refresh  Esc home  q quit';
  } else if (view === 'logs') {
    text = 'Logs: ↑/↓ scroll  PgUp/PgDn page  Enter older near top  Esc back  q quit';
  } else if (view === 'running' || busy) {
    text = 'Running action...';
  }

  return React.createElement(
    Box,
    { borderStyle: 'round', borderColor: BRAND_SECONDARY, paddingX: 1 },
    React.createElement(Text, { color: TEXT_MUTED }, text),
  );
}

function WorkloadsPane({ workloads, workloadIndex, workloadNotice, loadingWorkloads }) {
  const rows = workloads?.pods || [];
  const start = Math.max(0, Math.min(workloadIndex - 8, Math.max(0, rows.length - 16)));
  const visibleRows = rows.slice(start, start + 16);
  const headerLine = [
    truncateText('Pod', WORKLOAD_COLUMNS.pod),
    truncateText('Namespace', WORKLOAD_COLUMNS.namespace),
    truncateText('Status', WORKLOAD_COLUMNS.status),
    alignRight('Ready', WORKLOAD_COLUMNS.ready),
    alignRight('Restarts', WORKLOAD_COLUMNS.restarts),
    alignRight('Age', WORKLOAD_COLUMNS.age),
  ].join(' ');
  return React.createElement(
    Box,
    { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Workloads'),
    React.createElement(
      Text,
      { color: TEXT_MUTED },
      `Namespace: ${(workloads?.namespaces || []).join(', ') || 'msp'}`,
    ),
    React.createElement(Text, { color: TEXT_MUTED }, `Updated: ${workloads?.fetchedAt || 'pending...'}`),
    loadingWorkloads ? React.createElement(Text, { color: COLOR_WARN }, 'Refreshing workload inventory...') : null,
    React.createElement(Text, null, ''),
    React.createElement(Text, { color: BRAND_SECONDARY, bold: true }, headerLine),
    ...(rows.length
      ? visibleRows.map((pod, index) => {
          const absolute = start + index;
          const selected = absolute === workloadIndex;
          const pointer = selected ? '>' : ' ';

          return React.createElement(
            Text,
            {
              key: pod.key,
              backgroundColor: selected ? BRAND_SECONDARY : undefined,
              color: selected ? 'black' : undefined,
              bold: selected,
            },
            React.createElement(Text, { color: selected ? 'black' : BRAND_SECONDARY }, `${pointer} `),
            React.createElement(Text, { color: selected ? 'black' : TEXT_MUTED }, `${truncateText(pod.name, WORKLOAD_COLUMNS.pod)} `),
            React.createElement(Text, { color: selected ? 'black' : TEXT_MUTED }, `${truncateText(pod.namespace, WORKLOAD_COLUMNS.namespace)} `),
            React.createElement(Text, { color: selected ? 'black' : lineColor(pod.status) || TEXT_MUTED }, `${truncateText(pod.status, WORKLOAD_COLUMNS.status)} `),
            React.createElement(Text, { color: selected ? 'black' : TEXT_MUTED }, `${alignRight(pod.ready, WORKLOAD_COLUMNS.ready)} `),
            React.createElement(Text, { color: selected ? 'black' : TEXT_MUTED }, `${alignRight(pod.restarts, WORKLOAD_COLUMNS.restarts)} `),
            React.createElement(Text, { color: selected ? 'black' : TEXT_MUTED }, alignRight(pod.age, WORKLOAD_COLUMNS.age)),
          );
        })
      : [React.createElement(Text, { key: 'empty-workloads', color: TEXT_MUTED }, 'No appliance pods found.')]),
    ...(workloads?.errors || []).map((errorLine, index) =>
      React.createElement(Text, { key: `error-${index}`, color: COLOR_WARN }, errorLine),
    ),
    workloadNotice ? React.createElement(Text, { color: COLOR_WARN }, workloadNotice) : null,
  );
}

function LogPane({ selectedPod, logState, logNotice, loadingOlder, loadingLogs }) {
  const lines = logState?.lines || [];
  const top = Math.max(0, logState?.top ?? 0);
  const viewLines = lines.slice(top, top + LOG_VIEW_HEIGHT);
  const followMode = !!logState?.follow;

  return React.createElement(
    Box,
    { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, `Logs: ${selectedPod?.namespace || ''}/${selectedPod?.name || ''}`),
    React.createElement(
      Text,
      { color: TEXT_MUTED },
      `Lines: ${lines.length}  Follow: ${followMode ? 'on' : 'paused'}  Top: ${top + 1}`,
    ),
    loadingLogs ? React.createElement(Text, { color: COLOR_WARN }, 'Loading logs...') : null,
    loadingOlder ? React.createElement(Text, { color: COLOR_WARN }, 'Loading older log chunk...') : null,
    React.createElement(Text, null, ''),
    ...(viewLines.length
      ? viewLines.map((line, index) =>
          React.createElement(
            Text,
            {
              key: `${line.id}-${index}`,
              color: TEXT_MUTED,
            },
            line.text,
          ),
        )
      : [React.createElement(Text, { key: 'empty-logs', color: TEXT_MUTED }, 'No logs yet for selected pod.')]),
    logNotice ? React.createElement(Text, { color: COLOR_WARN }, logNotice) : null,
  );
}

function ProgressPane({ lines, maxLines = 12 }) {
  const shown = lines.slice(-maxLines);
  return React.createElement(
    Box,
    {
      borderStyle: 'round',
      borderColor: BRAND_SECONDARY,
      paddingX: 1,
      flexDirection: 'column',
      minHeight: Math.min(maxLines + 2, 28),
    },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Live Progress / Install Log'),
    ...(shown.length
      ? shown.map((line, index) =>
          React.createElement(
            Text,
            { key: `${index}-${line.slice(0, 8)}`, color: lineColor(line) || TEXT_MUTED },
            line,
          ),
        )
      : [React.createElement(Text, { key: 'empty', color: TEXT_MUTED }, 'No lifecycle output yet.')]),
  );
}

function RunningSummaryPane({ env, status, formType }) {
  const canonical = status?.canonical || status || {};
  const rollup = canonical.rollup || {};
  const operation = canonical.activeOperations?.[0]?.message || rollup.message || 'Waiting for lifecycle output...';
  const statusUrl = canonical.urls?.statusUrl || (env.nodeIp ? `http://${env.nodeIp}:8080` : 'unknown');
  const loginUrl = canonical.urls?.loginUrl || env.appUrl || (env.nodeIp ? `http://${env.nodeIp}:3000` : 'unknown');
  const blocker = canonical.topBlockers?.[0] || status?.topBlocker || null;
  return React.createElement(
    Box,
    { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column' },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, `${formType || 'Operation'} status`),
    React.createElement(Text, { color: BRAND_SECONDARY }, `Status page: ${statusUrl}`),
    React.createElement(Text, { color: TEXT_MUTED }, `Login URL: ${loginUrl}`),
    React.createElement(Text, { color: rollup.state === 'failed_action_required' ? COLOR_ERROR : BRAND_SECONDARY }, `Install state: ${rollup.state || canonical.status || 'unknown'}`),
    React.createElement(Text, { color: TEXT_MUTED }, `Current operation: ${operation}`),
    blocker
      ? React.createElement(Text, { color: COLOR_WARN }, `Blocker: ${blocker.reason || blocker.layer}`)
      : React.createElement(Text, { color: TEXT_MUTED }, 'Blocker: none detected yet'),
  );
}

function StatusPane({ status }) {
  const lines = shortStatusReport(status);
  return React.createElement(
    Box,
    {
      borderStyle: 'round',
      borderColor: BRAND_PRIMARY,
      paddingX: 1,
      flexDirection: 'column',
      minWidth: 0,
      width: undefined,
      flexBasis: 44,
      flexGrow: 1,
      flexShrink: 1,
    },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Status Dashboard'),
    ...renderLines(lines.slice(0, 20), 'dashboard', { fallbackColor: TEXT_MUTED }),
  );
}

function MainPane({
  view,
  env,
  status,
  formType,
  formValues,
  formIndex,
  siteIndex,
  pendingAction,
  notice,
  result,
  error,
  workloads,
  workloadIndex,
  workloadNotice,
  loadingWorkloads,
  selectedPod,
  logState,
  logNotice,
  loadingOlder,
  loadingLogs,
}) {
  if (view === 'site-select') {
    const sites = env.siteIds || [];
    return React.createElement(
      Box,
      { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
      React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Select Appliance Site'),
      ...sites.map((siteId, index) =>
        React.createElement(
          Text,
          {
            key: siteId,
            color: index === siteIndex ? BRAND_SECONDARY : TEXT_MUTED,
            bold: index === siteIndex,
          },
          `${index === siteIndex ? '>' : ' '} ${siteId}`,
        ),
      ),
      React.createElement(Text, null, ''),
      React.createElement(
        Text,
        { color: TEXT_MUTED },
        pendingAction
          ? `Select a discovered site to continue to ${pendingAction}.`
          : 'Select a discovered site to continue.',
      ),
    );
  }

  if (view === 'status') {
    const report = formatStatusReport(status);
    return React.createElement(
      Box,
      { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
      React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Status View'),
      ...renderLines(report.summary, 'status-summary', { fallbackColor: TEXT_MUTED }),
      React.createElement(Text, null, ''),
      React.createElement(Text, { bold: true, color: BRAND_SECONDARY }, 'Release'),
      ...renderLines(report.release, 'status-release', { fallbackColor: TEXT_MUTED }),
      React.createElement(Text, null, ''),
      React.createElement(Text, { bold: true, color: BRAND_SECONDARY }, 'Config Paths'),
      ...renderLines(report.paths, 'status-paths', { fallbackColor: TEXT_MUTED }),
    );
  }

  if (view === 'workloads') {
    return React.createElement(WorkloadsPane, {
      workloads,
      workloadIndex,
      workloadNotice,
      loadingWorkloads,
    });
  }

  if (view === 'logs') {
    return React.createElement(LogPane, {
      selectedPod,
      logState,
      logNotice,
      loadingOlder,
      loadingLogs,
    });
  }

  if (view === 'form') {
    const fields = formFields(formType, env);
    const title = `${formType} Form`;

    return React.createElement(
      Box,
      { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
      React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, title),
      formType === 'Reset'
        ? React.createElement(
            Box,
            { flexDirection: 'column' },
            React.createElement(Text, { color: COLOR_ERROR, bold: true }, `Target appliance: ${env.site.siteId}`),
            React.createElement(
              Text,
              { color: COLOR_ERROR },
              'Wipes namespace msp, namespace alga-system, and /var/mnt/alga-data/local-path-provisioner data.',
            ),
          )
        : null,
      formType === 'Repair Release'
        ? React.createElement(
            Text,
            { color: COLOR_WARN },
            'Repairs a stuck alga-core release by cleaning failed workloads and reconciling the HelmRelease.',
          )
        : null,
      React.createElement(FieldList, { fields, values: formValues, selectedIndex: formIndex }),
      notice ? React.createElement(Text, { color: COLOR_WARN }, notice) : null,
    );
  }

  if (view === 'confirm') {
    return React.createElement(
      Box,
      { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
      React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, `${formType} Confirmation`),
      ...Object.entries(formValues).map(([key, value]) =>
        React.createElement(Text, { key, color: TEXT_MUTED }, `${key}: ${String(value)}`),
      ),
      formType === 'Repair Release'
        ? React.createElement(Text, { color: COLOR_WARN }, 'Repair will clean up failed alga-core workloads before reconciling the release.')
        : null,
      React.createElement(Text, { color: TEXT_MUTED }, 'Press Enter to run, Esc to cancel.'),
    );
  }

  if (view === 'running') {
    return React.createElement(
      Box,
      { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
      React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, `${formType} Running`),
      React.createElement(Text, { color: TEXT_MUTED }, 'Streaming lifecycle progress in the panel below...'),
    );
  }

  return React.createElement(
    Box,
    { borderStyle: 'round', borderColor: BRAND_PRIMARY, paddingX: 1, flexDirection: 'column', flexGrow: 1 },
    React.createElement(Text, { bold: true, color: BRAND_PRIMARY }, 'Home'),
    React.createElement(Text, { color: TEXT_MUTED }, `Selected appliance: ${env.site?.siteId || 'unselected'}`),
    React.createElement(Text, { color: TEXT_MUTED }, `Node IP: ${env.nodeIp || 'unknown'}`),
    React.createElement(Text, { color: TEXT_MUTED }, `Current release: ${status?.release?.selectedReleaseVersion || 'unknown'}`),
    React.createElement(Text, { color: TEXT_MUTED }, 'Use arrow keys and Enter to launch a flow.'),
    notice ? React.createElement(Text, { color: COLOR_WARN }, notice) : null,
    result
      ? React.createElement(Text, { color: result.ok ? COLOR_OK : COLOR_ERROR, bold: true }, result.message)
      : null,
    error ? React.createElement(Text, { color: COLOR_ERROR }, error) : null,
  );
}

function formFields(formType, env) {
  if (formType === 'Reset') {
    return [{ key: 'challenge', label: `Type ${makeResetDefaults(env).expected}`, type: 'text' }];
  }

  if (formType === 'Repair Release') {
    return [
      { key: 'releaseName', label: 'Release Name', type: 'text' },
      { key: 'cleanupWorkloads', label: 'Cleanup Failed Workloads', type: 'select', options: YES_NO_OPTIONS },
    ];
  }

  if (formType === 'Support Bundle') {
    return [{ key: 'outputDir', label: 'Output Directory', type: 'text' }];
  }

  return [];
}

function initFormValues(formType, env) {
  if (formType === 'Reset') {
    return makeResetDefaults(env);
  }
  if (formType === 'Repair Release') {
    return makeRepairReleaseDefaults();
  }
  if (formType === 'Support Bundle') {
    return makeSupportBundleDefaults();
  }
  return {};
}

function normalizeChallenge(value) {
  return String(value || '')
    .trim()
    .replace(/\s+/g, ' ');
}

function TuiApp({ initialEnv, actions, onExit }) {
  const { exit } = useApp();
  const { stdout } = useStdout();
  const [env, setEnv] = useState(initialEnv);
  const [status, setStatus] = useState(null);
  const [view, setView] = useState('home');
  const [actionIndex, setActionIndex] = useState(0);
  const [siteIndex, setSiteIndex] = useState(0);
  const [pendingAction, setPendingAction] = useState('');
  const [formType, setFormType] = useState('');
  const [formValues, setFormValues] = useState({});
  const [formIndex, setFormIndex] = useState(0);
  const [progressLines, setProgressLines] = useState([]);
  const [busy, setBusy] = useState(false);
  const [notice, setNotice] = useState('');
  const [result, setResult] = useState(null);
  const [error, setError] = useState('');
  const [workloads, setWorkloads] = useState({ fetchedAt: null, namespaces: [], pods: [], errors: [] });
  const [loadingWorkloads, setLoadingWorkloads] = useState(false);
  const [workloadNotice, setWorkloadNotice] = useState('');
  const [workloadIndex, setWorkloadIndex] = useState(0);
  const [logState, setLogState] = useState({
    lines: [],
    top: 0,
    follow: true,
    tailLines: LOG_CHUNK_LINES,
    lastTimestamp: null,
  });
  const [loadingLogs, setLoadingLogs] = useState(false);
  const [loadingOlder, setLoadingOlder] = useState(false);
  const [logNotice, setLogNotice] = useState('');
  const [viewportWidth, setViewportWidth] = useState(stdout?.columns || process.stdout.columns || 140);
  const selectedPodRef = useRef(null);

  const compactLayout = viewportWidth < 140;
  const showProgressPane = busy || view === 'running' || progressLines.length > 0;
  const selectedPod = workloads.pods[workloadIndex] || null;

  useEffect(() => {
    if (!stdout || typeof stdout.on !== 'function') {
      return undefined;
    }

    let resizeTimer = null;
    const handleResize = () => {
      if (resizeTimer) {
        clearTimeout(resizeTimer);
      }

      resizeTimer = setTimeout(() => {
        setViewportWidth(stdout.columns || process.stdout.columns || 140);
      }, 80);
    };

    stdout.on('resize', handleResize);
    return () => {
      if (resizeTimer) {
        clearTimeout(resizeTimer);
      }
      stdout.off('resize', handleResize);
    };
  }, [stdout]);

  const refreshStatus = useMemo(
    () => async () => {
      if (!env.site) {
        setStatus(null);
        return;
      }

      try {
        const next = await actions.collectStatus(env);
        setStatus(next);
        setError('');
      } catch (err) {
        setError(err.message || String(err));
      }
    },
    [actions, env],
  );

  const refreshWorkloads = useMemo(
    () => async () => {
      if (!env.site) {
        setWorkloads({ fetchedAt: null, namespaces: [], pods: [], errors: [] });
        return;
      }

      setLoadingWorkloads(true);
      try {
        const next = await actions.listAppliancePods(env);
        setWorkloads(next);
        setWorkloadNotice('');
        setWorkloadIndex((current) => {
          if (selectedPodRef.current) {
            const byRef = next.pods.findIndex((entry) => entry.key === selectedPodRef.current.key);
            if (byRef >= 0) {
              return byRef;
            }
          }
          return clampIndex(current, next.pods.length);
        });
      } catch (err) {
        setWorkloadNotice(err.message || String(err));
      } finally {
        setLoadingWorkloads(false);
      }
    },
    [actions, env],
  );

  useEffect(() => {
    if (!env.siteSelectionRequired && env.site) {
      refreshStatus();
    }
  }, [env, refreshStatus]);

  useEffect(() => {
    selectedPodRef.current = selectedPod;
  }, [selectedPod]);

  useEffect(() => {
    if (view !== 'workloads') {
      return undefined;
    }

    refreshWorkloads();
    const timer = setInterval(() => {
      refreshWorkloads();
    }, WORKLOAD_REFRESH_MS);
    return () => clearInterval(timer);
  }, [refreshWorkloads, view]);

  useEffect(() => {
    if (view !== 'running') {
      return undefined;
    }

    refreshStatus();
    const timer = setInterval(() => {
      refreshStatus();
    }, 5000);
    return () => clearInterval(timer);
  }, [refreshStatus, view]);

  async function executeFormAction() {
    setBusy(true);
    setView('running');
    setNotice('');
    setResult(null);

    const append = (line) => {
      if (!line || !line.trim()) {
        return;
      }
      setProgressLines((prev) => [...prev.slice(-199), line]);
    };

    try {
      let output;
      if (formType === 'Reset') {
        output = await actions.runReset(env, {
          onProgress: (event) => append(mapProgressEvent(event)),
        });
      } else if (formType === 'Repair Release') {
        output = await actions.runRepairRelease(env, {
          ...formValues,
          cleanupWorkloads: formValues.cleanupWorkloads !== 'no',
          onProgress: (event) => append(mapProgressEvent(event)),
        });
      } else if (formType === 'Support Bundle') {
        output = await actions.runSupportBundle(env, {
          ...formValues,
          onProgress: (event) => append(mapProgressEvent(event)),
        });
      } else {
        output = { ok: true };
      }

      if (!output.ok) {
        const layer = output.failureLayer ? ` Failure layer: ${output.failureLayer}.` : '';
        setResult({
          ok: false,
          message: `${formType} failed.${layer}`.trim(),
        });
      } else {
        setResult({ ok: true, message: `${formType} completed successfully.` });
      }
    } catch (err) {
      setResult({ ok: false, message: `${formType} failed: ${err.message || String(err)}` });
    } finally {
      setBusy(false);
      setView('home');
      setFormType('');
      setFormValues({});
      setFormIndex(0);
      await refreshStatus();
    }
  }

  async function openLogViewerForPod(pod) {
    if (!pod) {
      setLogNotice('No pod selected.');
      return;
    }

    setLoadingLogs(true);
    setLogNotice('');
    try {
      const fetched = await actions.readPodLogsTail(env, pod, { tailLines: LOG_CHUNK_LINES });
      if (!fetched.ok) {
        setLogState({
          lines: [],
          top: 0,
          follow: true,
          tailLines: LOG_CHUNK_LINES,
          lastTimestamp: null,
        });
        setLogNotice(fetched.error || 'Unable to load pod logs.');
      } else {
        const lines = applyLogTail(fetched.lines);
        const lastTimestamp = fetched.lines.at(-1)?.timestamp || null;
        setLogState({
          lines,
          top: Math.max(0, lines.length - LOG_VIEW_HEIGHT),
          follow: true,
          tailLines: LOG_CHUNK_LINES,
          lastTimestamp,
        });
      }
      setView('logs');
    } catch (err) {
      setLogNotice(err.message || String(err));
    } finally {
      setLoadingLogs(false);
    }
  }

  async function loadOlderLogs() {
    if (!selectedPod || loadingOlder || loadingLogs) {
      return;
    }
    setLoadingOlder(true);
    setLogNotice('');
    try {
      const nextTail = (logState.tailLines || LOG_CHUNK_LINES) + LOG_CHUNK_LINES;
      const fetched = await actions.readPodLogsTail(env, selectedPod, { tailLines: nextTail });
      if (!fetched.ok) {
        setLogNotice(fetched.error || 'Unable to load older logs.');
        return;
      }
      setLogState((previous) => {
        const merged = prependOlderLines(previous.lines, fetched.lines);
        const shifted = Math.max(0, merged.length - previous.lines.length);
        const lastTimestamp = fetched.lines.at(-1)?.timestamp || previous.lastTimestamp || null;
        return {
          ...previous,
          lines: merged,
          tailLines: nextTail,
          top: previous.top + shifted,
          lastTimestamp,
        };
      });
    } catch (err) {
      setLogNotice(err.message || String(err));
    } finally {
      setLoadingOlder(false);
    }
  }

  useEffect(() => {
    if (view !== 'logs' || !selectedPod || !logState.follow) {
      return undefined;
    }

    const timer = setInterval(async () => {
      try {
        const fetched = await actions.readPodLogsSince(env, selectedPod, { sinceTime: logState.lastTimestamp });
        if (!fetched.ok || !fetched.lines.length) {
          return;
        }
        setLogState((previous) => {
          const lines = appendLiveLogLines(previous.lines, fetched.lines);
          const top = Math.max(0, lines.length - LOG_VIEW_HEIGHT);
          return {
            ...previous,
            lines,
            top,
            lastTimestamp: fetched.lines.at(-1)?.timestamp || previous.lastTimestamp || null,
          };
        });
      } catch {
        // Keep live follow resilient; surface hard failures only on direct user actions.
      }
    }, LOG_POLL_MS);

    return () => clearInterval(timer);
  }, [actions, env, logState.follow, logState.lastTimestamp, selectedPod, view]);

  function openAction(action) {
    setNotice('');
    setResult(null);

    if (!env.site) {
      if (!(env.siteIds || []).length) {
        setNotice('No appliance sites discovered. Run setup from the appliance web UI first.');
        setView('home');
        return;
      }
      setPendingAction(action);
      setSiteIndex(0);
      setView('site-select');
      return;
    }

    if (action === 'Status') {
      setView('status');
      return;
    }

    if (action === 'Workloads') {
      setView('workloads');
      return;
    }

    setFormType(action);
    setFormValues(initFormValues(action, env));
    setFormIndex(0);
    setView('form');
  }

  useInput((input, key) => {
    if (key.ctrl && input === 'c') {
      onExit(0);
      exit();
      return;
    }

    if (busy || view === 'running') {
      return;
    }

    if (input === 'q') {
      onExit(0);
      exit();
      return;
    }

    if (view === 'site-select') {
      const sites = env.siteIds || [];
      if (!sites.length) {
        setView('home');
        return;
      }
      if (key.escape) {
        setPendingAction('');
        setView('home');
        return;
      }
      if (key.upArrow || input === 'k') {
        setSiteIndex((value) => clampIndex(value - 1, sites.length));
        return;
      }
      if (key.downArrow || input === 'j') {
        setSiteIndex((value) => clampIndex(value + 1, sites.length));
        return;
      }
      if (key.return) {
        const selected = sites[siteIndex] || sites[0];
        if (!selected) {
          setNotice('No discovered site selected.');
          return;
        }
        const selectedEnv = selectDiscoveredSite(env, selected);
        setEnv(selectedEnv);
        const action = pendingAction;
        setPendingAction('');
        if (action === 'Status') {
          setView('status');
          return;
        }
        if (action === 'Workloads') {
          setView('workloads');
          return;
        }
        if (action) {
          setFormType(action);
          setFormValues(initFormValues(action, selectedEnv));
          setFormIndex(0);
          setView('form');
          return;
        }
        setView('home');
      }
      return;
    }

    if (view === 'status') {
      if (key.escape || input === 'h') {
        setView('home');
      }
      if (input === 'r') {
        refreshStatus();
      }
      return;
    }

    if (view === 'workloads') {
      if (key.escape || input === 'h') {
        setView('home');
        return;
      }
      if (key.upArrow || input === 'k') {
        setWorkloadIndex((value) => clampIndex(value - 1, workloads.pods.length));
        return;
      }
      if (key.downArrow || input === 'j') {
        setWorkloadIndex((value) => clampIndex(value + 1, workloads.pods.length));
        return;
      }
      if (input === 'r') {
        refreshWorkloads();
        return;
      }
      if (key.return) {
        openLogViewerForPod(selectedPod);
      }
      return;
    }

    if (view === 'logs') {
      if (key.escape || input === 'h') {
        setView('workloads');
        return;
      }

      if (key.pageUp) {
        setLogState((previous) => {
          const top = Math.max(0, previous.top - LOG_VIEW_HEIGHT);
          return {
            ...previous,
            top,
            follow: top + LOG_VIEW_HEIGHT >= previous.lines.length,
          };
        });
        return;
      }

      if (key.pageDown) {
        setLogState((previous) => {
          const maxTop = Math.max(0, previous.lines.length - LOG_VIEW_HEIGHT);
          const top = Math.min(maxTop, previous.top + LOG_VIEW_HEIGHT);
          return {
            ...previous,
            top,
            follow: top >= maxTop,
          };
        });
        return;
      }

      if (key.upArrow || input === 'k') {
        setLogState((previous) => {
          const top = Math.max(0, previous.top - 1);
          return {
            ...previous,
            top,
            follow: false,
          };
        });
        if ((logState.top || 0) <= 1) {
          loadOlderLogs();
        }
        return;
      }

      if (key.downArrow || input === 'j') {
        setLogState((previous) => {
          const maxTop = Math.max(0, previous.lines.length - LOG_VIEW_HEIGHT);
          const top = Math.min(maxTop, previous.top + 1);
          return {
            ...previous,
            top,
            follow: top >= maxTop,
          };
        });
        return;
      }

      if (key.return && (logState.top || 0) <= 1) {
        loadOlderLogs();
      }
      return;
    }

    if (view === 'home') {
      if (key.upArrow || input === 'k') {
        setActionIndex((value) => clampIndex(value - 1, ACTIONS.length));
        return;
      }
      if (key.downArrow || input === 'j') {
        setActionIndex((value) => clampIndex(value + 1, ACTIONS.length));
        return;
      }
      if (input === 'r') {
        refreshStatus();
        return;
      }
      if (key.return) {
        openAction(ACTIONS[actionIndex]);
      }
      return;
    }

    if (view === 'form') {
      const fields = formFields(formType, env);

      if (key.escape) {
        setView('home');
        setFormType('');
        setFormValues({});
        setFormIndex(0);
        return;
      }

      if (key.upArrow || input === 'k') {
        setFormIndex((value) => clampIndex(value - 1, fields.length));
        return;
      }
      if (key.downArrow || key.tab || input === 'j') {
        setFormIndex((value) => clampIndex(value + 1, fields.length));
        return;
      }

      const field = fields[formIndex];
      if (!field) {
        return;
      }

      if (field.type === 'select' && (key.leftArrow || key.rightArrow || input === 'h' || input === 'l')) {
        const delta = key.rightArrow || input === 'l' ? 1 : -1;
        setFormValues((prev) => ({
          ...prev,
          [field.key]: cycleOption(field.options || [], prev[field.key], delta),
        }));
        return;
      }

      if (key.return) {
        if (formType === 'Reset') {
          const expected = makeResetDefaults(env).expected;
          if (normalizeChallenge(formValues.challenge) !== normalizeChallenge(expected)) {
            setNotice(`Reset confirmation mismatch. Type ${expected}.`);
            return;
          }
        }
        setNotice('');
        setView('confirm');
        return;
      }

      if (field.type === 'text') {
        if (key.backspace || key.delete) {
          setFormValues((prev) => ({
            ...prev,
            [field.key]: String(prev[field.key] || '').slice(0, -1),
          }));
          return;
        }

        if (input && !key.meta && !key.ctrl) {
          setFormValues((prev) => ({
            ...prev,
            [field.key]: `${String(prev[field.key] || '')}${input}`,
          }));
        }
      }
      return;
    }

    if (view === 'confirm') {
      if (key.escape) {
        setView('form');
        return;
      }

      if (key.return) {
        executeFormAction();
      }
    }
  });

  if (view === 'running') {
    return React.createElement(
      Box,
      { flexDirection: 'column', width: '100%' },
      React.createElement(RunningSummaryPane, { env, status, formType }),
      React.createElement(Box, { marginTop: 1 }, React.createElement(ProgressPane, { lines: progressLines, maxLines: 24 })),
      React.createElement(Box, { marginTop: 1 }, React.createElement(HelpStrip, { view, busy, formName: formType || 'Operation' })),
    );
  }

  return React.createElement(
    Box,
    { flexDirection: 'column', width: '100%' },
    React.createElement(Header, { env, status }),
    React.createElement(
      Box,
      { flexDirection: compactLayout ? 'column' : 'row', marginTop: 1, width: '100%' },
      React.createElement(ActionNav, { selectedIndex: actionIndex, compactLayout }),
      React.createElement(
        Box,
        {
          marginLeft: compactLayout ? 0 : 1,
          marginTop: compactLayout ? 1 : 0,
          flexGrow: 1,
          flexBasis: compactLayout ? undefined : 0,
          flexShrink: 1,
          minWidth: 0,
        },
        React.createElement(MainPane, {
          view,
          env,
          status,
          formType,
          formValues,
          formIndex,
          siteIndex,
          pendingAction,
          notice,
          result,
          error,
          workloads,
          workloadIndex,
          workloadNotice,
          loadingWorkloads,
          selectedPod,
          logState,
          logNotice,
          loadingOlder,
          loadingLogs,
        }),
      ),
      React.createElement(
        Box,
        {
          marginLeft: compactLayout ? 0 : 1,
          marginTop: compactLayout ? 1 : 0,
          flexGrow: compactLayout ? 0 : 1,
          flexBasis: compactLayout ? undefined : 0,
          flexShrink: 1,
          minWidth: 0,
        },
        React.createElement(StatusPane, { status }),
      ),
    ),
    showProgressPane
      ? React.createElement(Box, { marginTop: 1 }, React.createElement(ProgressPane, { lines: progressLines, maxLines: 10 }))
      : null,
    React.createElement(Box, { marginTop: 1 }, React.createElement(HelpStrip, { view, busy, formName: formType || 'Home' })),
  );
}

function createTuiActions(overrides = {}) {
  return {
    collectStatus: overrides.collectStatus || collectStatus,
    runRepairRelease: overrides.runRepairRelease || runRepairRelease,
    runReset: overrides.runReset || runReset,
    runSupportBundle: overrides.runSupportBundle || runSupportBundle,
    listAppliancePods: overrides.listAppliancePods || listAppliancePods,
    readPodLogsTail: overrides.readPodLogsTail || readPodLogsTail,
    readPodLogsSince: overrides.readPodLogsSince || readPodLogsSince,
  };
}

export async function runTui(env, options = {}) {
  const actions = createTuiActions(options);
  let exitCode = 0;
  const enterAlt = process.stdout.isTTY;

  if (enterAlt) {
    process.stdout.write('\u001B[?1049h\u001B[H');
  }

  try {
    const app = render(
      React.createElement(TuiApp, {
        initialEnv: env,
        actions,
        onExit: (code) => {
          exitCode = code;
        },
      }),
      {
        exitOnCtrlC: true,
      },
    );

    await app.waitUntilExit();
    return exitCode;
  } finally {
    if (enterAlt) {
      process.stdout.write('\u001B[?1049l');
    }
  }
}

export { TuiApp, createTuiActions, formFields, initFormValues, mapProgressEvent };
