import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { HTMLAttributes } from 'react';
import { cn } from '../../lib/utils';
import { Check, ChevronDown, ChevronRight, CheckCircle2, Loader2, ShieldAlert, X, XCircle, Slash } from 'lucide-react';
import { Button } from '../ui/button';
import { Badge } from '../ui/badge';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
import { useI18n } from '../../application/i18n/I18nProvider';

/**
 * Pull the user-meaningful shell command out of the tool-call args.
 *
 * Different tool surfaces hand us different shapes:
 *   - Netcatty's own `terminal_execute` MCP tool → `{command: "<string>"}`
 *   - Codex `local_shell`                      → `{command: ["zsh","-lc","<full>"]}`
 *   - Codex command_execution (SDK)             → `{command: "/bin/zsh -lc '<full>'"}`
 *   - Claude `Bash`                             → `{command: "<string>"}`
 *
 * The SDK form is a STRING that wraps the real command in `<shell> -lc '<full>'`,
 * so we unwrap that wrapper too (the array branch already did the equivalent) —
 * otherwise the outer shell quotes leak into the title.
 *
 * And under the "Skill + CLI" integration, the agent's shell tool wraps a
 * call to our internal `netcatty-tool-cli` binary, so the real intent is one
 * level deeper:
 *
 *   netcatty-tool-cli exec --session <id> --chat-session <id> -- <real-cmd>
 *
 * We unwrap both layers so the chat panel shows what the user actually
 * cares about (the remote command), not Codex's wrapper title which is
 * just the local path to the CLI binary.
 */
export function extractDisplayCommand(args: Record<string, unknown> | undefined): string | null {
  if (!args) return null;
  const raw = (args as { command?: unknown }).command;

  let cmdString: string;
  if (typeof raw === 'string') {
    if (!raw) return null;
    cmdString = raw;
  } else if (Array.isArray(raw) && raw.length > 0) {
    const isShellWrap =
      raw.length >= 3 &&
      /(?:^|\/)(sh|bash|zsh|fish|ash|dash)$/.test(String(raw[0] ?? '')) &&
      /^-l?c$/.test(String(raw[1] ?? ''));
    cmdString = isShellWrap
      ? String(raw[raw.length - 1] ?? '')
      : raw.map((p) => String(p)).join(' ');
  } else {
    return null;
  }

  // Unwrap a STRING shell wrapper, e.g. Codex SDK's `/bin/zsh -lc '<full>'`.
  // The array branch above already extracts the inner command; the string form
  // (codex command_execution) does not, so strip `<shell> -l?c <quote>…<quote>`
  // here. Without this the outer quote leaks into the netcatty-cli title below.
  const strWrap = cmdString.match(
    /^(?:\S*\/)?(?:sh|bash|zsh|fish|ash|dash)\s+-l?c\s+(['"])([\s\S]*)\1\s*$/,
  );
  if (strWrap) cmdString = strWrap[2];

  // Netcatty CLI wrapper extraction.
  const cliIdx = cmdString.indexOf('netcatty-tool-cli');
  if (cliIdx >= 0) {
    const afterCli = cmdString
      .slice(cliIdx + 'netcatty-tool-cli'.length)
      .replace(/^["']?\s*/, '');
    const subMatch = afterCli.match(/^(\S+)/);
    const sub = subMatch ? subMatch[1] : '';

    if (sub === 'exec' || sub === 'job-start') {
      // Pull out the command after the ` -- ` separator.
      const dashIdx = afterCli.indexOf(' -- ');
      if (dashIdx >= 0) {
        let inner = afterCli.slice(dashIdx + 4).trim();
        if (
          inner.length >= 2 &&
          ((inner[0] === '"' && inner.endsWith('"')) ||
            (inner[0] === "'" && inner.endsWith("'")))
        ) {
          inner = inner.slice(1, -1);
        }
        return inner;
      }
    }
    if (sub === 'job-poll') return 'netcatty: poll job';
    if (sub === 'job-stop') return 'netcatty: stop job';
    if (sub === 'session') return 'netcatty: inspect session';
    if (sub === 'env') return 'netcatty: list sessions';
    if (sub === 'status') return 'netcatty: status';
    if (sub) return `netcatty: ${sub}`;
  }

  return cmdString;
}

/**
 * Format tool result for display. Extracts stdout/stderr from structured
 * command results for terminal-like output.
 */
function formatToolResult(result: unknown): string {
  let parsed = result;

  if (typeof parsed === 'string') {
    try {
      const obj = JSON.parse(parsed);
      if (obj && typeof obj === 'object') parsed = obj;
    } catch {
      return parsed;
    }
  }

  if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
    const obj = parsed as Record<string, unknown>;
    if (typeof obj.stdout === 'string' || typeof obj.stderr === 'string') {
      const parts: string[] = [];
      if (typeof obj.stdout === 'string' && obj.stdout) parts.push(obj.stdout);
      if (typeof obj.stderr === 'string' && obj.stderr) parts.push(obj.stderr);
      if (typeof obj.exitCode === 'number' && obj.exitCode !== 0) {
        parts.push(`exit code: ${obj.exitCode}`);
      }
      if (parts.length > 0) return parts.join('\n');
    }
  }

  if (typeof parsed === 'string') return parsed;
  return JSON.stringify(parsed, null, 2);
}

export interface ToolCallProps extends HTMLAttributes<HTMLDivElement> {
  name: string;
  className?: string;
  args?: Record<string, unknown>;
  result?: unknown;
  isError?: boolean;
  isLoading?: boolean;
  isInterrupted?: boolean;
  /** Approval state for this tool call (from the approval gate). */
  approvalStatus?: 'pending' | 'approved' | 'denied';
  /** Called when user approves this tool call. */
  onApprove?: () => void;
  /** Called when user rejects this tool call. */
  onReject?: () => void;
}

export const ToolCall = ({
  name, args, result, isError, isLoading, isInterrupted,
  approvalStatus, onApprove, onReject,
  className, ...props
}: ToolCallProps) => {
  const { t } = useI18n();
  const [expanded, setExpanded] = useState(false);
  const cardRef = useRef<HTMLDivElement>(null);
  const approveBtnRef = useRef<HTMLButtonElement>(null);
  const [responded, setResponded] = useState(false);

  const isPendingApproval = approvalStatus === 'pending' && !responded;

  const handleApprove = useCallback(() => {
    if (!isPendingApproval) return;
    setResponded(true);
    onApprove?.();
  }, [isPendingApproval, onApprove]);

  const handleReject = useCallback(() => {
    if (!isPendingApproval) return;
    setResponded(true);
    onReject?.();
  }, [isPendingApproval, onReject]);

  // Keyboard: Enter = approve, Escape = reject (when pending)
  const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
    if (!isPendingApproval) return;
    if (e.key === 'Enter') { e.preventDefault(); handleApprove(); }
    else if (e.key === 'Escape') { e.preventDefault(); handleReject(); }
  }, [isPendingApproval, handleApprove, handleReject]);

  // Auto-focus and auto-scroll when approval is pending
  useEffect(() => {
    if (isPendingApproval && cardRef.current) {
      cardRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
      // Small delay to let the UI render, then expand and focus
      setExpanded(true);
      setTimeout(() => approveBtnRef.current?.focus(), 100);
    }
  }, [isPendingApproval]);

  // Reset responded state when approvalStatus changes (e.g. new approval)
  useEffect(() => {
    if (approvalStatus === 'pending') setResponded(false);
  }, [approvalStatus]);

  // Border/bg color based on approval status
  const borderClass = approvalStatus === 'pending'
    ? 'border-yellow-500/30 bg-yellow-500/[0.04]'
    : approvalStatus === 'approved'
      ? 'border-green-500/20 bg-green-500/[0.03]'
      : approvalStatus === 'denied'
        ? 'border-red-500/20 bg-red-500/[0.03]'
        : 'border-border/25 bg-muted/10';
  const statusIconClass = 'shrink-0';

  const statusIcon = approvalStatus === 'pending' ? (
    <ShieldAlert size={12} className={cn('text-yellow-500/70', statusIconClass)} />
  ) : isLoading ? (
    <Loader2 size={12} className={cn('animate-spin text-blue-400/70', statusIconClass)} />
  ) : isInterrupted ? (
    <Slash size={12} className={cn('text-muted-foreground/55', statusIconClass)} />
  ) : isError ? (
    <XCircle size={12} className={cn('text-red-400/70', statusIconClass)} />
  ) : result !== undefined ? (
    <CheckCircle2 size={12} className={cn('text-green-400/70', statusIconClass)} />
  ) : null;

  return (
    <div
      ref={cardRef}
      tabIndex={isPendingApproval ? 0 : undefined}
      onKeyDown={isPendingApproval ? handleKeyDown : undefined}
      className={cn('rounded-md border overflow-hidden text-[12px] outline-none', borderClass, className)}
      {...props}
    >
      <button
        type="button"
        onClick={() => setExpanded(e => !e)}
        className="w-full flex items-center gap-2 px-3 py-1.5 hover:bg-muted/20 transition-colors cursor-pointer"
      >
        {expanded
          ? <ChevronDown size={12} className="text-muted-foreground/40 shrink-0" />
          : <ChevronRight size={12} className="text-muted-foreground/40 shrink-0" />
        }
        {(() => {
          const displayCmd = extractDisplayCommand(args);
          if (displayCmd) {
            return (
              <Tooltip>
                <TooltipTrigger asChild>
                  <span className="font-mono text-muted-foreground/70 truncate cursor-default">
                    <span className="text-muted-foreground/40">$ </span>{displayCmd}
                  </span>
                </TooltipTrigger>
                <TooltipContent>{displayCmd}</TooltipContent>
              </Tooltip>
            );
          }
          return <span className="font-mono text-muted-foreground/70 truncate">{name}</span>;
        })()}
        <span className="flex-1" />
        {/* Approval badge for resolved approvals */}
        {approvalStatus === 'approved' && (
          <Badge className="text-[10px] px-1.5 py-0 bg-green-600/20 text-green-400 border-green-600/30">
            {t('ai.chat.toolApproved')}
          </Badge>
        )}
        {approvalStatus === 'denied' && (
          <Badge className="text-[10px] px-1.5 py-0 bg-red-600/20 text-red-400 border-red-600/30">
            {t('ai.chat.toolDenied')}
          </Badge>
        )}
        {statusIcon}
      </button>

      {expanded && (
        <div className="border-t border-border/20">
          {args && Object.keys(args).length > 0 && (
            <div className="px-3 py-2">
              <div className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground/30 mb-1">Arguments</div>
              <pre className="max-h-64 overflow-auto text-[11px] font-mono text-muted-foreground/50 whitespace-pre [overflow-wrap:normal]">
                {JSON.stringify(args, null, 2)}
              </pre>
            </div>
          )}

          {/* Inline approval buttons */}
          {isPendingApproval && (
            <div className="px-3 py-2 border-t border-border/20">
              <div className="flex items-center justify-between">
                <span className="text-[10px] text-muted-foreground/30">
                  {t('ai.chat.toolApprovalHint')}
                </span>
                <div className="flex items-center gap-1.5">
                  <Button
                    variant="outline"
                    size="sm"
                    className="h-6 px-2 text-[11px] border-red-500/20 text-red-400/80 hover:bg-red-500/10 hover:text-red-400"
                    onClick={handleReject}
                  >
                    <X size={11} className="mr-0.5" />
                    {t('ai.chat.reject')}
                  </Button>
                  <Button
                    ref={approveBtnRef}
                    size="sm"
                    className="h-6 px-2.5 text-[11px] bg-green-600/80 hover:bg-green-600 text-white"
                    onClick={handleApprove}
                  >
                    <Check size={11} className="mr-0.5" />
                    {t('ai.chat.approve')}
                  </Button>
                </div>
              </div>
            </div>
          )}

          {result !== undefined && (
            <div className="px-3 py-2 border-t border-border/20">
              <div className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground/30 mb-1">Result</div>
              <pre className={cn(
                'max-h-64 overflow-auto text-[11px] font-mono whitespace-pre [overflow-wrap:normal]',
                isError ? 'text-red-400/60' : 'text-muted-foreground/50',
              )}>
                {formatToolResult(result)}
              </pre>
            </div>
          )}
          {isInterrupted && result === undefined && (
            <div className="px-3 py-2 border-t border-border/20">
              <div className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground/30 mb-1">Status</div>
              <div className="text-[11px] text-muted-foreground/50">
                Interrupted
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
};
