import { Box, Text, useStdout } from "ink";
import { StatusBar } from "./StatusBar.tsx";
import { FilterBar } from "./FilterBar.tsx";
import { ProjectList } from "./ProjectList.tsx";
import { HelpOverlay } from "./HelpOverlay.tsx";
import { ConfirmDialog } from "./ConfirmDialog.tsx";
import { CloneDialog } from "./CloneDialog.tsx";
import { RepoDetailModal } from "./RepoDetailModal.tsx";
import { CommandPalette } from "./CommandPalette.tsx";
import { AddDirectoryDialog } from "./AddDirectoryDialog.tsx";
import { FilterOptionsOverlay } from "./FilterOptionsOverlay.tsx";
import { useStore, useFilteredUnifiedRepos, useSelectedUnifiedRepos } from "../state/store.tsx";
import { useConfirmDialogActions } from "../hooks/useConfirmDialogActions.ts";
import { startAction, endAction, setMessage } from "../state/actions.ts";
import { executeCommand } from "../operations/commands.ts";
import { batchPull, batchPush, batchFetch } from "../operations/batch.ts";
import { errorToString } from "../utils/errors.ts";
import { openInEditor, openInBrowser } from "../services/editor.ts";
import type { Project } from "../types/index.ts";
import { UI } from "../constants.ts";
import type { GitforestConfig, CommandConfig, ConfirmDialogState, UnifiedRepo } from "../types/index.ts";

interface LayoutProps {
  config: GitforestConfig;
  onRefresh: (options?: { forceRefresh?: boolean }) => Promise<void>;
  onClone?: (repos: UnifiedRepo[], targetDir: string, useSSH: boolean) => Promise<void>;
  onAddDirectory?: (path: string, maxDepth: number, label: string) => Promise<{ success: boolean; error?: string }>;
}

export function Layout({ config, onRefresh, onClone, onAddDirectory }: LayoutProps) {
  const { state, dispatch } = useStore();
  const { mode, modal } = state;
  const confirmDialog = modal.kind === "confirm" ? modal.data : null;
  const cloneDialog = modal.kind === "clone" ? modal.data : null;
  const detailModal = modal.kind === "detail" ? modal.data : null;
  const addDirectoryDialog = modal.kind === "add-directory" ? modal.data : null;
  const filteredRepos = useFilteredUnifiedRepos();
  const selectedRepos = useSelectedUnifiedRepos();
  const { stdout } = useStdout();

  const { handleConfirm, handleCancel } = useConfirmDialogActions({
    config,
    onRefresh,
  });

  // Calculate available height
  const terminalHeight = stdout?.rows ?? UI.DEFAULT_TERMINAL_HEIGHT;
  const listHeight = terminalHeight - UI.LAYOUT_OVERHEAD;

  // Calculate stats for status bar
  const dirtyCount = filteredRepos.filter((r) => r.local?.status?.isDirty).length;
  const unpushedCount = filteredRepos.filter((r) => r.local?.status?.isAhead).length;
  const localOnlyCount = filteredRepos.filter((r) => r.source === "local").length;
  const githubOnlyCount = filteredRepos.filter((r) => r.source === "github").length;
  const syncedCount = filteredRepos.filter((r) => r.source === "both").length;

  // Clone dialog handlers
  const handleCloneConfirm = async (targetDir: string, useSSH: boolean) => {
    const repos = cloneDialog?.repos;
    dispatch({ type: "CLOSE_MODAL" });
    if (onClone && repos) {
      await onClone(repos, targetDir, useSSH);
    }
  };

  const handleCloneCancel = () => {
    dispatch({ type: "CLOSE_MODAL" });
  };

  const handleCloneSelectDir = (index: number) => {
    dispatch({ type: "UPDATE_MODAL", payload: { kind: "clone", data: { selectedDirIndex: index } } });
  };

  const handleCloneToggleSSH = () => {
    if (cloneDialog) {
      dispatch({ type: "UPDATE_MODAL", payload: { kind: "clone", data: { useSSH: !cloneDialog.useSSH } } });
    }
  };

  // Show help overlay
  if (mode === "help") {
    return (
      <Box flexDirection="column" height={terminalHeight}>
        <HelpOverlay commands={config.commands} />
      </Box>
    );
  }

  // Show command palette
  if (mode === "command-palette") {
    return (
      <Box flexDirection="column" height={terminalHeight} justifyContent="center" alignItems="center">
        <CommandPalette
          commands={config.commands}
          selectedRepos={selectedRepos}
          onClose={() => dispatch({ type: "SET_MODE", payload: "normal" })}
        />
      </Box>
    );
  }

  // Show confirm dialog
  if (confirmDialog) {
    return (
      <Box flexDirection="column" height={terminalHeight} justifyContent="center" alignItems="center">
        <ConfirmDialog
          title={confirmDialog.title}
          message={confirmDialog.message}
          items={confirmDialog.items}
          showVisibilityToggle={confirmDialog.showVisibilityToggle}
          defaultVisibility={config.github.defaultVisibility}
          onConfirm={handleConfirm}
          onCancel={handleCancel}
        />
      </Box>
    );
  }

  // Show clone dialog
  if (cloneDialog) {
    return (
      <Box flexDirection="column" height={terminalHeight} justifyContent="center" alignItems="center">
        <CloneDialog
          repos={cloneDialog.repos}
          directories={cloneDialog.directories}
          selectedDirIndex={cloneDialog.selectedDirIndex}
          useSSH={cloneDialog.useSSH}
          onConfirm={handleCloneConfirm}
          onCancel={handleCloneCancel}
          onSelectDir={handleCloneSelectDir}
          onToggleSSH={handleCloneToggleSSH}
        />
      </Box>
    );
  }

  // Show detail modal
  if (detailModal) {
    const handleDetailClose = () => {
      dispatch({ type: "CLOSE_MODAL" });
    };

    const handleDetailAction = async (action: string) => {
      const repo = detailModal.repo;
      if (!repo) return;

      switch (action) {
        case "clone": {
          dispatch({
            type: "OPEN_MODAL",
            payload: {
              kind: "clone",
              data: {
                repos: [repo],
                directories: config.directories,
                selectedDirIndex: 0,
                useSSH: true,
              },
            },
          });
          return;
        }
        case "primary": {
          if (repo.localPath) {
            // Fallthrough to editor logic
            await handleDetailAction("editor");
          } else if (repo.github?.htmlUrl) {
            await handleDetailAction("browser");
          }
          return;
        }
        case "push":
          if (repo.local && repo.local.status?.isAhead) {
            dispatch(startAction("Pushing"));
            const result = await batchPush([repo.local satisfies Project]);
            dispatch(endAction());
            dispatch(setMessage(result.failed > 0 ? `Push failed: ${result.results[0]?.error ?? "unknown"}` : `Pushed ${repo.name}`));
            await onRefresh({ forceRefresh: true });
          }
          break;
        case "pull":
          if (repo.local && repo.local.status?.hasRemote) {
            dispatch(startAction("Pulling"));
            const result = await batchPull([repo.local satisfies Project]);
            dispatch(endAction());
            dispatch(setMessage(result.failed > 0 ? `Pull failed: ${result.results[0]?.error ?? "unknown"}` : `Pulled ${repo.name}`));
            await onRefresh({ forceRefresh: true });
          }
          break;
        case "fetch":
          if (repo.local && repo.local.status?.hasRemote) {
            dispatch(startAction("Fetching"));
            const result = await batchFetch([repo.local satisfies Project]);
            dispatch(endAction());
            dispatch(setMessage(result.failed > 0 ? `Fetch failed: ${result.results[0]?.error ?? "unknown"}` : `Fetched ${repo.name}`));
            await onRefresh({ forceRefresh: true });
          }
          break;
        case "browser":
          if (repo.github?.htmlUrl) {
            const browserResult = await openInBrowser(repo.github.htmlUrl);
            if (!browserResult.success) {
              dispatch(setMessage(`Failed to open browser: ${browserResult.error}`));
            }
          } else {
            dispatch(setMessage("No GitHub URL available for this repository"));
          }
          break;
        case "editor":
          if (repo.localPath) {
            const editorResult = await openInEditor(repo.localPath, config);
            if (!editorResult.success) {
              dispatch(setMessage(`Failed to open editor: ${editorResult.error}`));
            }
          } else {
            dispatch(setMessage("No local path available for this repository"));
          }
          break;
      }
    };

    const handleDetailScroll = (offset: number) => {
      dispatch({ type: "UPDATE_MODAL", payload: { kind: "detail", data: { readmeScrollOffset: offset } } });
    };

    const handleDetailCommand = async (command: CommandConfig) => {
      const repo = detailModal.repo;
      if (!repo) return;

      const projectPath = repo.localPath || repo.local?.path;
      if (!projectPath) {
        dispatch(setMessage("No local path for this repo"));
        return;
      }

      if (command.confirm) {
        const dialogState: ConfirmDialogState = {
          operation: "command",
          title: `Run "${command.name}"`,
          message: `Run \`${command.command}\` on:`,
          items: [repo.name],
          projectPaths: [projectPath],
          showVisibilityToggle: false,
          command,
        };
        // CLOSE_MODAL then OPEN_MODAL (the detail modal is replaced by the
        // confirm modal — one modal slot, sequential dispatches).
        dispatch({ type: "OPEN_MODAL", payload: { kind: "confirm", data: dialogState } });
        return;
      }

      dispatch(startAction(`Running: ${command.name}`));
      try {
        const result = await executeCommand(command, projectPath);
        dispatch(endAction());
        if (result.success) {
          const shortOutput = result.output && result.output.length > UI.OUTPUT_TRUNCATION_LENGTH
            ? result.output.slice(0, UI.OUTPUT_TRUNCATION_LENGTH) + "..."
            : result.output || "Done";
          dispatch(setMessage(`${command.name}: ${shortOutput}`));
        } else {
          dispatch(setMessage(`${command.name} failed: ${result.error}`));
        }
      } catch (error) {
        dispatch(endAction());
        dispatch(setMessage(`${command.name} failed: ${errorToString(error)}`));
      }
    };

    return (
      <Box flexDirection="column" height={terminalHeight}>
        <RepoDetailModal
          repo={detailModal.repo}
          readmeContent={detailModal.readmeContent}
          readmeLoading={detailModal.readmeLoading}
          readmeError={detailModal.readmeError}
          scrollOffset={detailModal.readmeScrollOffset}
          commands={config.commands}
          onClose={handleDetailClose}
          onAction={handleDetailAction}
          onScroll={handleDetailScroll}
          onCommand={handleDetailCommand}
        />
      </Box>
    );
  }

  // Show add directory dialog
  if (addDirectoryDialog) {
    const handleAddDirConfirm = async (path: string, maxDepth: number, label: string) => {
      if (onAddDirectory) {
        const result = await onAddDirectory(path, maxDepth, label);
        if (result.success) {
          dispatch({ type: "CLOSE_MODAL" });
          dispatch(setMessage(`Added directory: ${path}`));
          await onRefresh({ forceRefresh: true });
        } else {
          // The hook owns the rich flow state, so we report failures through
          // the message bar rather than re-injecting an error into modal data.
          dispatch(setMessage(`Failed to add directory: ${result.error ?? "unknown error"}`));
        }
      }
    };

    return (
      <Box flexDirection="column" height={terminalHeight} justifyContent="center" alignItems="center">
        <AddDirectoryDialog
          initialPath={addDirectoryDialog.initialPath}
          onConfirm={handleAddDirConfirm}
          onCancel={() => dispatch({ type: "CLOSE_MODAL" })}
        />
      </Box>
    );
  }

  // Show filter options overlay
  if (mode === "filter-options") {
    return (
      <Box flexDirection="column" height={terminalHeight} justifyContent="center" alignItems="center">
        <FilterOptionsOverlay />
      </Box>
    );
  }

  return (
    <Box flexDirection="column" height={terminalHeight}>
      {/* Header */}
      <Box borderStyle="single" borderBottom borderColor="gray" paddingX={1}>
        <Text bold color="cyan">
          gitforest
        </Text>
        <Text> - Git Repository Manager</Text>
      </Box>

      {/* Filter bar */}
      <Box paddingX={1} paddingY={0}>
        <FilterBar />
      </Box>

      {/* Project list */}
      <Box flexGrow={1} flexDirection="column" paddingX={1}>
        <ProjectList height={listHeight} />
      </Box>

      {/* Status bar */}
      <Box borderStyle="single" borderTop borderColor="gray" paddingX={1}>
        <StatusBar
          projectCount={filteredRepos.length}
          dirtyCount={dirtyCount}
          unpushedCount={unpushedCount}
          localOnlyCount={localOnlyCount}
          githubOnlyCount={githubOnlyCount}
          syncedCount={syncedCount}
        />
      </Box>
    </Box>
  );
}
