import fs from "fs";
import path from "path";
import React, { createElement, useEffect, useMemo, useState } from "react";
import { Box, Text, useApp, useInput } from "ink";
import { formatDuration } from "../../runner/formatting.mjs";
import { formatFileDetail, loadCurrentRunArtifact } from "../viewer.mjs";

export function WatchApp({ productDir, serviceFilter = null }) {
  const { exit } = useApp();
  const [artifact, setArtifact] = useState(() => safeLoadArtifact(productDir));
  const [view, setView] = useState("files");
  const [selectedFileIndex, setSelectedFileIndex] = useState(0);
  const [selectedSetupIndex, setSelectedSetupIndex] = useState(0);

  const files = useMemo(() => collectFiles(artifact, serviceFilter), [artifact, serviceFilter]);
  const setupOperations = useMemo(
    () => collectSetupOperations(artifact, serviceFilter),
    [artifact, serviceFilter]
  );
  const selectedFile = files[Math.min(selectedFileIndex, Math.max(0, files.length - 1))] || null;
  const selectedSetup =
    setupOperations[Math.min(selectedSetupIndex, Math.max(0, setupOperations.length - 1))] || null;

  useEffect(() => {
    const timer = setInterval(() => {
      setArtifact(safeLoadArtifact(productDir));
    }, 1_000);
    return () => clearInterval(timer);
  }, [productDir]);

  useEffect(() => {
    setSelectedFileIndex((current) => Math.min(current, Math.max(0, files.length - 1)));
  }, [files.length]);

  useEffect(() => {
    setSelectedSetupIndex((current) => Math.min(current, Math.max(0, setupOperations.length - 1)));
  }, [setupOperations.length]);

  useInput((input, key) => {
    if (input === "q") {
      exit();
      return;
    }
    if (input === "r") {
      setArtifact(safeLoadArtifact(productDir));
      return;
    }
    if (key.tab || input === "s") {
      setView((current) => {
        if (current === "files" && setupOperations.length > 0) return "setup";
        return "files";
      });
      return;
    }
    if (key.downArrow) {
      if (view === "setup") {
        setSelectedSetupIndex((current) => Math.min(current + 1, Math.max(0, setupOperations.length - 1)));
      } else {
        setSelectedFileIndex((current) => Math.min(current + 1, Math.max(0, files.length - 1)));
      }
      return;
    }
    if (key.upArrow) {
      if (view === "setup") {
        setSelectedSetupIndex((current) => Math.max(0, current - 1));
      } else {
        setSelectedFileIndex((current) => Math.max(0, current - 1));
      }
    }
  });

  if (!artifact) {
    return createElement(Text, null, `No run artifact found in ${productDir}`);
  }

  return createElement(
    Box,
    { flexDirection: "column" },
    createElement(
      Text,
      null,
      `testkit watch · q quit · r reload · tab toggle · ${artifact.run.status} · ${formatDuration(artifact.run.durationMs)}`
    ),
    createElement(
      Box,
      { marginTop: 1 },
      createElement(
        Box,
        { width: "40%", flexDirection: "column", marginRight: 2 },
        createElement(Text, null, view === "setup" ? "Setup" : "Files"),
        ...(view === "setup"
          ? renderSetupEntries(setupOperations, selectedSetupIndex)
          : renderFileEntries(files, selectedFileIndex))
      ),
      createElement(
        Box,
        { width: "60%", flexDirection: "column" },
        createElement(Text, null, "Details"),
        ...(view === "setup"
          ? formatSetupDetail(productDir, selectedSetup)
              .slice(0, 28)
              .map((line, index) => createElement(Text, { key: `${index}:${line}` }, line))
          : selectedFile
          ? formatFileDetail(productDir, artifact, selectedFile, { logTail: 8 })
              .slice(0, 28)
              .map((line, index) => createElement(Text, { key: `${index}:${line}` }, line))
          : [createElement(Text, { key: "empty" }, "No file results")])
      )
    )
  );
}

function safeLoadArtifact(productDir) {
  try {
    return loadCurrentRunArtifact(productDir);
  } catch {
    return null;
  }
}

function collectFiles(runArtifact, serviceFilter) {
  if (!runArtifact) return [];
  const entries = [];
  for (const service of runArtifact.services || []) {
    if (serviceFilter && service.name !== serviceFilter) continue;
    for (const suite of service.suites || []) {
      for (const file of suite.files || []) {
        entries.push({ service, suite, file });
      }
    }
  }
  const failed = entries.filter((entry) => entry.file.status === "failed");
  return (failed.length > 0 ? failed : entries).sort((left, right) =>
    left.file.path.localeCompare(right.file.path)
  );
}

function renderFileEntries(files, selectedIndex) {
  return files.map((entry, index) => {
    const prefix = index === selectedIndex ? ">" : " ";
    return createElement(
      Text,
      { key: `${entry.service.name}:${entry.file.path}` },
      `${prefix} ${entry.file.status.toUpperCase().padEnd(7)} ${entry.file.path}`
    );
  });
}

function collectSetupOperations(runArtifact, serviceFilter) {
  if (!runArtifact) return [];
  return [...(runArtifact.setup?.operations || [])]
    .filter((entry) => !serviceFilter || entry.serviceName === serviceFilter)
    .sort((left, right) => {
      return (
        setupStatusRank(left.status) - setupStatusRank(right.status) ||
        String(left.serviceName || "").localeCompare(String(right.serviceName || "")) ||
        String(left.startedAt || "").localeCompare(String(right.startedAt || "")) ||
        String(left.stage || "").localeCompare(String(right.stage || ""))
      );
    });
}

function renderSetupEntries(operations, selectedIndex) {
  return operations.map((entry, index) => {
    const prefix = index === selectedIndex ? ">" : " ";
    const duration = entry.durationMs == null ? "" : ` ${formatDuration(entry.durationMs)}`;
    return createElement(
      Text,
      { key: entry.id },
      `${prefix} ${String(entry.status || "").toUpperCase().padEnd(7)} ${entry.serviceName} ${entry.stage}${duration}`
    );
  });
}

function formatSetupDetail(productDir, operation) {
  if (!operation) return ["No setup operations"];
  const lines = [
    `Service: ${operation.serviceName}`,
    `Stage: ${operation.stage}`,
    `Status: ${operation.status}`,
  ];
  if (operation.durationMs != null) {
    lines.push(`Duration: ${formatDuration(operation.durationMs)}`);
  }
  if (operation.summary) {
    lines.push(`Summary: ${operation.summary}`);
  }
  if (operation.error) {
    lines.push(`Error: ${operation.error}`);
  }
  if (operation.logRef?.path) {
    lines.push("");
    lines.push("Log:");
    lines.push(`  ${operation.logRef.path}`);
    const absolutePath = path.join(productDir, operation.logRef.path);
    for (const line of readTailSafe(absolutePath, 12)) {
      lines.push(`  ${line}`);
    }
  }
  return lines;
}

function readTailSafe(absolutePath, maxLines) {
  try {
    return fs.readFileSync(absolutePath, "utf8")
      .split(/\r?\n/)
      .filter(Boolean)
      .slice(-maxLines);
  } catch {
    return [];
  }
}

function setupStatusRank(status) {
  if (status === "failed") return 1;
  if (status === "running") return 2;
  if (status === "passed") return 3;
  if (status === "cached") return 4;
  return 5;
}
