import type { FileSystemNode } from "./types";
import { getCurrentDirectory, resolvePath } from "./fs";

export interface CommandContext {
  currentPath: string;
  fileSystem: { [key: string]: FileSystemNode };
  setCurrentPath: (path: string) => void;
  setIsTrainRunning: (running: boolean) => void;
  setTrainPosition: (position: number) => void;
}

export function executeCommand(input: string, context: CommandContext): string {
  const trimmedInput = input.trim();
  if (!trimmedInput) return "";

  const [command, ...args] = trimmedInput.split(" ");

  switch (command.toLowerCase()) {
    case "help":
      return handleHelp();
    case "ls":
      return handleLs(args, context);
    case "cd":
      return handleCd(args, context);
    case "pwd":
      return handlePwd(context);
    case "cat":
      return handleCat(args, context);
    case "tree":
      return handleTree(context);
    case "clear":
      return "";
    case "whoami":
      return handleWhoami();
    case "open":
      return handleOpen(args);
    case "sl":
      return handleSl(context);
    default:
      return `${command}: command not found. Type 'help' for available commands.`;
  }
}

function handleHelp(): string {
  return `Available commands:
          ls [path]     - List directory contents
          cd <path>     - Change directory
          cat <file>    - Display file contents
          pwd           - Show current directory
          clear         - Clear terminal
          tree          - Show directory structure
          whoami        - Display user info
          open <path>   - Open page in browser (simulated)
          help          - Show this help message

        Navigation:
          Use "cd .." to go up one directory
          Use "cd /" to go to root directory
          File paths can be relative or absolute
          Use TAB for auto-completion

        Examples:
          ls
          cd resume
          cat about.txt
          open /resume
          open /talks
          cat /talks/README.txt
          cd social
          cat /tech/README.txt`;
}

function handleLs(args: string[], context: CommandContext): string {
  const { currentPath, fileSystem } = context;
  const targetPath = args[0] ? resolvePath(currentPath, args[0]) : currentPath;
  const pathParts = targetPath.split("/").filter((part: string) => part !== "");
  let target = fileSystem["/"];

  for (const part of pathParts) {
    if (
      target?.children &&
      target.children[part] &&
      target.children[part].type === "directory"
    ) {
      target = target.children[part];
    } else if (pathParts.length > 0) {
      return `ls: cannot access '${targetPath}': No such file or directory`;
    }
  }

  if (!target?.children) {
    return `ls: cannot access '${targetPath}': Not a directory`;
  }

  const items = Object.values(target.children)
    .map((item) => {
      const color = item.type === "directory" ? "\x1b[34m" : "\x1b[0m";
      const suffix = item.type === "directory" ? "/" : "";
      return `${color}${item.name}${suffix}\x1b[0m`;
    })
    .join("  ");

  return items || "Directory is empty";
}

function handleCd(args: string[], context: CommandContext): string {
  const { currentPath, fileSystem, setCurrentPath } = context;
  const targetPath = args[0] ? resolvePath(currentPath, args[0]) : "/";
  const pathParts = targetPath.split("/").filter((part: string) => part !== "");
  let current = fileSystem["/"];

  for (const part of pathParts) {
    if (
      current?.children &&
      current.children[part] &&
      current.children[part].type === "directory"
    ) {
      current = current.children[part];
    } else {
      return `cd: no such file or directory: ${targetPath}`;
    }
  }

  setCurrentPath(targetPath || "/");
  return "";
}

function handlePwd(context: CommandContext): string {
  return context.currentPath || "/";
}

function handleCat(args: string[], context: CommandContext): string {
  const { currentPath, fileSystem } = context;

  if (!args[0]) {
    return "cat: missing file argument";
  }

  const filePath = resolvePath(currentPath, args[0]);
  const pathParts = filePath.split("/").filter((part: string) => part !== "");
  const fileName = pathParts.pop();

  let current = fileSystem["/"];
  for (const part of pathParts) {
    if (
      current?.children &&
      current.children[part] &&
      current.children[part].type === "directory"
    ) {
      current = current.children[part];
    } else {
      return `cat: ${filePath}: No such file or directory`;
    }
  }

  if (!fileName || !current?.children || !current.children[fileName]) {
    return `cat: ${filePath}: No such file or directory`;
  }

  const file = current.children[fileName];
  if (file.type !== "file") {
    return `cat: ${filePath}: Is a directory`;
  }

  return file.content || "";
}

function handleTree(context: CommandContext): string {
  const { fileSystem } = context;

  const buildTree = (
    node: FileSystemNode,
    prefix: string = "",
    isLast: boolean = true,
  ): string => {
    let result = "";
    if (!node.children) return result;

    const entries = Object.entries(node.children);
    entries.forEach(([name, child], index) => {
      const isLastChild = index === entries.length - 1;
      const connector = isLastChild ? "└── " : "├── ";
      const color = child.type === "directory" ? "\x1b[34m" : "\x1b[0m";
      const suffix = child.type === "directory" ? "/" : "";

      result += `${prefix}${connector}${color}${name}${suffix}\x1b[0m\n`;

      if (child.type === "directory") {
        const newPrefix = prefix + (isLastChild ? "    " : "│   ");
        result += buildTree(child, newPrefix, isLastChild);
      }
    });

    return result;
  };

  return ".\n" + buildTree(fileSystem["/"]);
}

function handleWhoami(): string {
  return "guest@atri.dad";
}

function handleOpen(args: string[]): string {
  const path = args[0];
  if (!path) {
    return "open: missing path argument";
  }

  let url = "";
  if (path === "/resume" || path.startsWith("/resume")) {
    url = "/resume";
  } else if (path === "/projects" || path.startsWith("/projects")) {
    url = "/projects";
  } else if (path === "/posts" || path.startsWith("/posts")) {
    url = "/posts";
  } else if (path === "/talks" || path.startsWith("/talks")) {
    url = "/talks";
  } else if (path === "/" || path === "/about.txt") {
    url = "/";
  } else {
    return `open: cannot open '${path}': No associated page`;
  }

  window.open(url, "_blank");
  return `Opening ${url} in new tab...`;
}

function handleSl(context: CommandContext): string {
  const { setIsTrainRunning, setTrainPosition } = context;

  setIsTrainRunning(true);
  setTrainPosition(100);

  const animateTrain = () => {
    let position = 100;
    const interval = setInterval(() => {
      position -= 1.5;
      setTrainPosition(position);

      if (position < -50) {
        clearInterval(interval);
        setIsTrainRunning(false);
      }
    }, 60);
  };

  setTimeout(animateTrain, 100);
  return "";
}
