import React, { useEffect, useReducer } from "react";
import { Box, Text } from "ink";
import Spinner from "ink-spinner";
import SelectInput from "ink-select-input";

import { startRun } from "../lib/runner.js";

const MAX_LOG_LINES = 12;

function reducer(state, action) {
  switch (action.type) {
    case "step-start":
      return {
        ...state,
        currentIndex: action.index,
        statuses: { ...state.statuses, [action.index]: "running" },
      };
    case "step-done":
      return {
        ...state,
        statuses: { ...state.statuses, [action.index]: action.status },
        errors:
          action.error != null
            ? { ...state.errors, [action.index]: action.error }
            : state.errors,
      };
    case "log": {
      const next = [...state.log, action];
      if (next.length > MAX_LOG_LINES * 4) {
        next.splice(0, next.length - MAX_LOG_LINES * 4);
      }
      return { ...state, log: next };
    }
    case "done":
      return { ...state, finished: true, success: action.success, cancelled: !!action.cancelled };
    default:
      return state;
  }
}

export default function RunLog({ nav, plan, origin }) {
  const [state, dispatch] = useReducer(reducer, {
    statuses: {},
    errors: {},
    currentIndex: -1,
    log: [],
    finished: false,
    success: false,
    cancelled: false,
  });

  useEffect(() => {
    const run = startRun(plan.steps);
    run.on("event", (event) => {
      dispatch(event);
    });
    return () => run.cancel();
  }, [plan]);

  const recentLog = state.log.slice(-MAX_LOG_LINES);

  return (
    <Box flexDirection="column">
      <Text bold>{plan.title}</Text>
      <Box flexDirection="column" marginTop={1}>
        {plan.steps.map((step, index) => (
          <StepRow
            key={index}
            index={index}
            step={step}
            status={state.statuses[index]}
            error={state.errors[index]}
          />
        ))}
      </Box>
      {recentLog.length > 0 ? (
        <Box flexDirection="column" marginTop={1} borderStyle="single" borderColor="gray" paddingX={1}>
          {recentLog.map((entry, i) => (
            <Text key={i} color={entry.stream === "stderr" ? "yellow" : undefined} dimColor>
              {entry.text}
            </Text>
          ))}
        </Box>
      ) : null}
      {state.finished ? <FinishedFooter state={state} nav={nav} origin={origin} /> : (
        <Box marginTop={1}>
          <Text color="cyan">
            <Spinner type="dots" />
          </Text>
          <Text> running…</Text>
        </Box>
      )}
    </Box>
  );
}

function StepRow({ index, step, status, error }) {
  const symbol = symbolFor(status);
  const color = colorFor(status);
  return (
    <Box>
      <Box width={3}>
        <Text color={color}>{symbol}</Text>
      </Box>
      <Box width={3}>
        <Text dimColor>{`${index + 1}.`}</Text>
      </Box>
      <Box flexDirection="column">
        <Text>{step.label}</Text>
        {error ? <Text color="red">  {error}</Text> : null}
      </Box>
    </Box>
  );
}

function symbolFor(status) {
  switch (status) {
    case "running":
      return "◐";
    case "ok":
      return "✓";
    case "failed":
      return "✗";
    case "cancelled":
      return "⊘";
    default:
      return "·";
  }
}

function colorFor(status) {
  switch (status) {
    case "running":
      return "cyan";
    case "ok":
      return "green";
    case "failed":
      return "red";
    case "cancelled":
      return "yellow";
    default:
      return "gray";
  }
}

function FinishedFooter({ state, nav, origin }) {
  const items = [
    { label: "Back to main menu", value: "home" },
    ...(origin === "frp" ? [{ label: "Back to FRP menu", value: "frp" }] : []),
    { label: "Quit", value: "quit" },
  ];

  const handleSelect = (item) => {
    if (item.value === "home") {
      nav.home();
    } else if (item.value === "frp") {
      nav.replace({ name: "frp" });
      // collapse stack down to just main + frp
      // (replace just swaps top; we want clean stack)
    } else if (item.value === "quit") {
      nav.exit();
    }
  };

  return (
    <Box flexDirection="column" marginTop={1}>
      {state.cancelled ? (
        <Text color="yellow">Cancelled.</Text>
      ) : state.success ? (
        <Text color="green">All steps completed successfully.</Text>
      ) : (
        <Text color="red">Run failed.</Text>
      )}
      <Box marginTop={1}>
        <SelectInput items={items} onSelect={handleSelect} />
      </Box>
    </Box>
  );
}
