/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../../types.js';
import { CliSpinner } from '../CliSpinner.js';
import {
  SHELL_COMMAND_NAME,
  SHELL_NAME,
  TOOL_STATUS,
  SHELL_FOCUS_HINT_DELAY_MS,
} from '../../constants.js';
import { theme } from '../../semantic-colors.js';
import {
  type Config,
  SHELL_TOOL_NAME,
  isCompletedAskUserTool,
  type ToolResultDisplay,
  CoreToolCallStatus,
} from '@google/gemini-cli-core';
import { useInactivityTimer } from '../../hooks/useInactivityTimer.js';
import { formatCommand } from '../../key/keybindingUtils.js';
import { Command } from '../../key/keyBindings.js';

export const STATUS_INDICATOR_WIDTH = 3;

/**
 * Returns true if the tool name corresponds to a shell tool.
 */
export function isShellTool(name: string): boolean {
  const normalized = name.toLowerCase();
  return (
    name === SHELL_COMMAND_NAME ||
    name === SHELL_NAME ||
    name === SHELL_TOOL_NAME ||
    normalized === 'shell'
  );
}

/**
 * Returns true if the shell tool call is currently focusable.
 */
export function isThisShellFocusable(
  name: string,
  status: CoreToolCallStatus,
  config?: Config,
): boolean {
  return !!(
    isShellTool(name) &&
    status === CoreToolCallStatus.Executing &&
    config?.getEnableInteractiveShell()
  );
}

/**
 * Returns true if this specific shell tool call is currently focused.
 */
export function isThisShellFocused(
  name: string,
  status: CoreToolCallStatus,
  ptyId?: number,
  activeShellPtyId?: number | null,
  embeddedShellFocused?: boolean,
): boolean {
  return !!(
    isShellTool(name) &&
    status === CoreToolCallStatus.Executing &&
    ptyId === activeShellPtyId &&
    embeddedShellFocused
  );
}

/**
 * Hook to manage focus hint state.
 */
export function useFocusHint(
  isThisShellFocusable: boolean,
  isThisShellFocused: boolean,
  resultDisplay: ToolResultDisplay | undefined,
) {
  const [userHasFocused, setUserHasFocused] = useState(false);

  // Derive a stable reset key for the inactivity timer. For strings and arrays
  // (shell output), we use the length to capture updates without referential
  // identity issues or expensive deep comparisons.
  const resetKey =
    typeof resultDisplay === 'string'
      ? resultDisplay.length
      : Array.isArray(resultDisplay)
        ? resultDisplay.length
        : !!resultDisplay;

  const showFocusHint = useInactivityTimer(
    isThisShellFocusable,
    resetKey,
    SHELL_FOCUS_HINT_DELAY_MS,
  );

  useEffect(() => {
    if (isThisShellFocused) {
      setUserHasFocused(true);
    }
  }, [isThisShellFocused]);

  const shouldShowFocusHint =
    isThisShellFocusable && (showFocusHint || userHasFocused);

  return { shouldShowFocusHint };
}

/**
 * Component to render the focus hint.
 */
export const FocusHint: React.FC<{
  shouldShowFocusHint: boolean;
  isThisShellFocused: boolean;
}> = ({ shouldShowFocusHint, isThisShellFocused }) => {
  if (!shouldShowFocusHint) {
    return null;
  }

  return (
    <Box marginLeft={1} flexShrink={0}>
      <Text color={isThisShellFocused ? theme.ui.focus : theme.ui.active}>
        {isThisShellFocused
          ? `(${formatCommand(Command.UNFOCUS_SHELL_INPUT)} to unfocus)`
          : `(${formatCommand(Command.FOCUS_SHELL_INPUT)} to focus)`}
      </Text>
    </Box>
  );
};

export type TextEmphasis = 'high' | 'medium' | 'low';

type ToolStatusIndicatorProps = {
  status: CoreToolCallStatus;
  name: string;
  isFocused?: boolean;
};

export const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
  status: coreStatus,
  name,
  isFocused,
}) => {
  const status = mapCoreStatusToDisplayStatus(coreStatus);
  const isShell = isShellTool(name);
  const statusColor = isFocused
    ? theme.ui.focus
    : isShell
      ? theme.ui.active
      : theme.status.warning;

  return (
    <Box minWidth={STATUS_INDICATOR_WIDTH}>
      {status === ToolCallStatus.Pending && (
        <Text color={theme.status.success}>{TOOL_STATUS.PENDING}</Text>
      )}
      {status === ToolCallStatus.Executing && (
        <Text color={statusColor}>
          <CliSpinner type="toggle" />
        </Text>
      )}
      {status === ToolCallStatus.Success && (
        <Text color={theme.status.success} aria-label={'Success:'}>
          {TOOL_STATUS.SUCCESS}
        </Text>
      )}
      {status === ToolCallStatus.Confirming && (
        <Text color={statusColor} aria-label={'Confirming:'}>
          {TOOL_STATUS.CONFIRMING}
        </Text>
      )}
      {status === ToolCallStatus.Canceled && (
        <Text color={statusColor} aria-label={'Canceled:'} bold>
          {TOOL_STATUS.CANCELED}
        </Text>
      )}
      {status === ToolCallStatus.Error && (
        <Text color={theme.status.error} aria-label={'Error:'} bold>
          {TOOL_STATUS.ERROR}
        </Text>
      )}
    </Box>
  );
};

type ToolInfoProps = {
  name: string;
  description: string;
  status: CoreToolCallStatus;
  emphasis: TextEmphasis;
  progressMessage?: string;
  originalRequestName?: string;
  isExpanded?: boolean;
};

export const ToolInfo: React.FC<ToolInfoProps> = ({
  name,
  description,
  status: coreStatus,
  emphasis,
  progressMessage: _progressMessage,
  originalRequestName,
  isExpanded = false,
}) => {
  const status = mapCoreStatusToDisplayStatus(coreStatus);
  const nameColor = React.useMemo<string>(() => {
    switch (emphasis) {
      case 'high':
        return theme.text.primary;
      case 'medium':
        return theme.text.primary;
      case 'low':
        return theme.text.secondary;
      default: {
        const exhaustiveCheck: never = emphasis;
        return exhaustiveCheck;
      }
    }
  }, [emphasis]);

  // Hide description for completed Ask User tools (the result display speaks for itself)
  const isCompletedAskUser = isCompletedAskUserTool(name, status);

  return (
    <Box
      overflow="hidden"
      height={isExpanded ? undefined : 1}
      flexGrow={1}
      flexShrink={1}
    >
      <Text
        strikethrough={status === ToolCallStatus.Canceled}
        wrap={isExpanded ? 'wrap' : 'truncate'}
      >
        <Text color={nameColor} bold>
          {name}
        </Text>
        {originalRequestName && originalRequestName !== name && (
          <Text color={theme.text.secondary} italic>
            {' '}
            (redirection from {originalRequestName})
          </Text>
        )}
        {!isCompletedAskUser && (
          <>
            {' '}
            <Text color={theme.text.secondary}>{description}</Text>
          </>
        )}
      </Text>
    </Box>
  );
};

export interface McpProgressIndicatorProps {
  progress: number;
  total?: number;
  message?: string;
  barWidth: number;
}

export const McpProgressIndicator: React.FC<McpProgressIndicatorProps> = ({
  progress,
  total,
  message,
  barWidth,
}) => {
  const percentage =
    total && total > 0
      ? Math.min(100, Math.round((progress / total) * 100))
      : null;

  let rawFilled: number;
  if (total && total > 0) {
    rawFilled = Math.round((progress / total) * barWidth);
  } else {
    rawFilled = Math.floor(progress) % (barWidth + 1);
  }

  const filled = Math.max(
    0,
    Math.min(Number.isFinite(rawFilled) ? rawFilled : 0, barWidth),
  );
  const empty = Math.max(0, barWidth - filled);
  const progressBar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);

  return (
    <Box flexDirection="column">
      <Box>
        <Text color={theme.text.accent}>
          {progressBar} {percentage !== null ? `${percentage}%` : `${progress}`}
        </Text>
      </Box>
      {message && (
        <Text color={theme.text.secondary} wrap="truncate">
          {message}
        </Text>
      )}
    </Box>
  );
};

export const TrailingIndicator: React.FC = () => (
  <Text color={theme.text.primary} wrap="truncate">
    {' '}
    ←
  </Text>
);
