"use client";

import type { KeyboardEvent, RefObject } from "react";
import { useCallback, useState, useEffect, useRef } from "react";
import { ORCHESTRATOR_COMMANDS } from "@/hooks/useOrchestratorCommands";

export const AVAILABLE_MODELS = [
  { id: "llama3.2", label: "Llama 3.2" },
  { id: "claude-3.5-sonnet", label: "Claude 3.5 Sonnet" },
  { id: "gpt-4o", label: "GPT-4o" },
  { id: "deepseek-r1", label: "DeepSeek R1" },
] as const;

export type ModelId = (typeof AVAILABLE_MODELS)[number]["id"];

const STORAGE_KEY_MODEL = "chat:selectedModel";
const STORAGE_KEY_TEMPERATURE = "chat:temperature";
const STORAGE_KEY_MAX_TOKENS = "chat:maxTokens";

export const DEFAULT_TEMPERATURE = 0.7;
export const DEFAULT_MAX_TOKENS = 4096;
export const DEFAULT_MODEL: ModelId = "llama3.2";

function loadStoredModel(): ModelId {
  try {
    const stored = localStorage.getItem(STORAGE_KEY_MODEL);
    if (stored && AVAILABLE_MODELS.some((m) => m.id === stored)) {
      return stored as ModelId;
    }
  } catch {
    // localStorage not available
  }
  return DEFAULT_MODEL;
}

function loadStoredTemperature(): number {
  try {
    const stored = localStorage.getItem(STORAGE_KEY_TEMPERATURE);
    if (stored !== null) {
      const parsed = parseFloat(stored);
      if (!isNaN(parsed) && parsed >= 0 && parsed <= 2) {
        return parsed;
      }
    }
  } catch {
    // localStorage not available
  }
  return DEFAULT_TEMPERATURE;
}

function loadStoredMaxTokens(): number {
  try {
    const stored = localStorage.getItem(STORAGE_KEY_MAX_TOKENS);
    if (stored !== null) {
      const parsed = parseInt(stored, 10);
      if (!isNaN(parsed) && parsed >= 100 && parsed <= 32000) {
        return parsed;
      }
    }
  } catch {
    // localStorage not available
  }
  return DEFAULT_MAX_TOKENS;
}

interface ChatInputProps {
  onSend: (message: string) => void;
  disabled?: boolean;
  inputRef?: RefObject<HTMLTextAreaElement | null>;
  isStreaming?: boolean;
  onStopStreaming?: () => void;
  onModelChange?: (model: ModelId) => void;
  onTemperatureChange?: (temperature: number) => void;
  onMaxTokensChange?: (maxTokens: number) => void;
  onSuggestionFill?: (text: string) => void;
  externalValue?: string;
}

export function ChatInput({
  onSend,
  disabled,
  inputRef,
  isStreaming = false,
  onStopStreaming,
  onModelChange,
  onTemperatureChange,
  onMaxTokensChange,
  externalValue,
}: ChatInputProps): React.JSX.Element {
  const [message, setMessage] = useState("");
  const [version, setVersion] = useState<string | null>(null);
  const [selectedModel, setSelectedModel] = useState<ModelId>(DEFAULT_MODEL);
  const [temperature, setTemperature] = useState<number>(DEFAULT_TEMPERATURE);
  const [maxTokens, setMaxTokens] = useState<number>(DEFAULT_MAX_TOKENS);
  const [isModelDropdownOpen, setIsModelDropdownOpen] = useState(false);
  const [isParamsOpen, setIsParamsOpen] = useState(false);

  // Command autocomplete state
  const [commandSuggestions, setCommandSuggestions] = useState<typeof ORCHESTRATOR_COMMANDS>([]);
  const [highlightedCommandIndex, setHighlightedCommandIndex] = useState(0);
  const commandDropdownRef = useRef<HTMLDivElement>(null);

  const modelDropdownRef = useRef<HTMLDivElement>(null);
  const paramsDropdownRef = useRef<HTMLDivElement>(null);

  // Stable refs for callbacks so the mount effect stays dependency-free
  const onModelChangeRef = useRef(onModelChange);
  onModelChangeRef.current = onModelChange;
  const onTemperatureChangeRef = useRef(onTemperatureChange);
  onTemperatureChangeRef.current = onTemperatureChange;
  const onMaxTokensChangeRef = useRef(onMaxTokensChange);
  onMaxTokensChangeRef.current = onMaxTokensChange;

  // Load persisted values from localStorage on mount only
  useEffect(() => {
    const storedModel = loadStoredModel();
    const storedTemperature = loadStoredTemperature();
    const storedMaxTokens = loadStoredMaxTokens();

    setSelectedModel(storedModel);
    setTemperature(storedTemperature);
    setMaxTokens(storedMaxTokens);

    // Notify parent of initial values via refs to avoid stale closure
    onModelChangeRef.current?.(storedModel);
    onTemperatureChangeRef.current?.(storedTemperature);
    onMaxTokensChangeRef.current?.(storedMaxTokens);
  }, []);

  // Sync external value (e.g. from suggestion clicks)
  useEffect(() => {
    if (externalValue !== undefined) {
      setMessage(externalValue);
    }
  }, [externalValue]);

  useEffect(() => {
    interface VersionData {
      version?: string;
      commit?: string;
    }

    fetch("/version.json")
      .then((res) => res.json() as Promise<VersionData>)
      .then((data) => {
        if (data.version) {
          const fullVersion = data.commit ? `${data.version}+${data.commit}` : data.version;
          setVersion(fullVersion);
        }
      })
      .catch(() => {
        // Silently fail - version display is non-critical
      });
  }, []);

  // Update command autocomplete suggestions when message changes
  useEffect(() => {
    const trimmed = message.trimStart();
    if (!trimmed.startsWith("/")) {
      setCommandSuggestions([]);
      setHighlightedCommandIndex(0);
      return;
    }

    // If the input contains a space, a command has been completed — no suggestions
    if (trimmed.includes(" ")) {
      setCommandSuggestions([]);
      setHighlightedCommandIndex(0);
      return;
    }

    const typedCommand = trimmed.toLowerCase();

    // Build flat list including aliases
    const matches = ORCHESTRATOR_COMMANDS.filter((cmd) => {
      if (cmd.name.startsWith(typedCommand)) return true;
      if (cmd.aliases?.some((a) => a.startsWith(typedCommand))) return true;
      return false;
    });

    setCommandSuggestions(matches);
    setHighlightedCommandIndex(0);
  }, [message]);

  // Close dropdowns on outside click
  useEffect(() => {
    const handleClickOutside = (e: MouseEvent): void => {
      if (modelDropdownRef.current && !modelDropdownRef.current.contains(e.target as Node)) {
        setIsModelDropdownOpen(false);
      }
      if (paramsDropdownRef.current && !paramsDropdownRef.current.contains(e.target as Node)) {
        setIsParamsOpen(false);
      }
      if (commandDropdownRef.current && !commandDropdownRef.current.contains(e.target as Node)) {
        setCommandSuggestions([]);
      }
    };
    document.addEventListener("mousedown", handleClickOutside);
    return (): void => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);

  const handleSubmit = useCallback(() => {
    if (message.trim() && !disabled && !isStreaming) {
      onSend(message);
      setMessage("");
    }
  }, [message, onSend, disabled, isStreaming]);

  const handleStop = useCallback(() => {
    onStopStreaming?.();
  }, [onStopStreaming]);

  const acceptCommand = useCallback((cmdName: string): void => {
    setMessage(cmdName + " ");
    setCommandSuggestions([]);
    setHighlightedCommandIndex(0);
  }, []);

  const handleKeyDown = useCallback(
    (e: KeyboardEvent<HTMLTextAreaElement>) => {
      // Command autocomplete navigation
      if (commandSuggestions.length > 0) {
        if (e.key === "ArrowDown") {
          e.preventDefault();
          setHighlightedCommandIndex((prev) =>
            prev < commandSuggestions.length - 1 ? prev + 1 : 0
          );
          return;
        }
        if (e.key === "ArrowUp") {
          e.preventDefault();
          setHighlightedCommandIndex((prev) =>
            prev > 0 ? prev - 1 : commandSuggestions.length - 1
          );
          return;
        }
        if (
          e.key === "Tab" ||
          (e.key === "Enter" && !e.shiftKey && commandSuggestions.length > 0)
        ) {
          e.preventDefault();
          const selected = commandSuggestions[highlightedCommandIndex];
          if (selected) {
            acceptCommand(selected.name);
          }
          return;
        }
        if (e.key === "Escape") {
          e.preventDefault();
          setCommandSuggestions([]);
          return;
        }
      }

      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();
        handleSubmit();
      }
      if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
        e.preventDefault();
        handleSubmit();
      }
    },
    [handleSubmit, commandSuggestions, highlightedCommandIndex, acceptCommand]
  );

  const handleModelSelect = useCallback(
    (model: ModelId): void => {
      setSelectedModel(model);
      try {
        localStorage.setItem(STORAGE_KEY_MODEL, model);
      } catch {
        // ignore
      }
      onModelChange?.(model);
      setIsModelDropdownOpen(false);
    },
    [onModelChange]
  );

  const handleTemperatureChange = useCallback(
    (value: number): void => {
      setTemperature(value);
      try {
        localStorage.setItem(STORAGE_KEY_TEMPERATURE, value.toString());
      } catch {
        // ignore
      }
      onTemperatureChange?.(value);
    },
    [onTemperatureChange]
  );

  const handleMaxTokensChange = useCallback(
    (value: number): void => {
      setMaxTokens(value);
      try {
        localStorage.setItem(STORAGE_KEY_MAX_TOKENS, value.toString());
      } catch {
        // ignore
      }
      onMaxTokensChange?.(value);
    },
    [onMaxTokensChange]
  );

  const selectedModelLabel =
    AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.label ?? selectedModel;

  const characterCount = message.length;
  const maxCharacters = 4000;
  const isNearLimit = characterCount > maxCharacters * 0.9;
  const isOverLimit = characterCount > maxCharacters;
  const isInputDisabled = disabled ?? false;

  return (
    <div className="space-y-2">
      {/* Model Selector + Params Row */}
      <div className="flex items-center gap-2">
        {/* Model Selector */}
        <div className="relative" ref={modelDropdownRef}>
          <button
            type="button"
            onClick={() => {
              setIsModelDropdownOpen((prev) => !prev);
              setIsParamsOpen(false);
            }}
            className="flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors hover:bg-black/5 focus:outline-none focus:ring-2"
            style={{
              borderColor: "rgb(var(--border-default))",
              backgroundColor: "rgb(var(--surface-1))",
              color: "rgb(var(--text-secondary))",
            }}
            aria-label={`Model: ${selectedModelLabel}. Click to change`}
            aria-expanded={isModelDropdownOpen}
            aria-haspopup="listbox"
            title="Select AI model"
          >
            <svg
              className="h-3 w-3 flex-shrink-0"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
              strokeWidth={2}
              aria-hidden="true"
            >
              <circle cx="12" cy="12" r="3" />
              <path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83" />
            </svg>
            <span>{selectedModelLabel}</span>
            <svg
              className={`h-3 w-3 transition-transform ${isModelDropdownOpen ? "rotate-180" : ""}`}
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
              strokeWidth={2}
              aria-hidden="true"
            >
              <path d="M6 9l6 6 6-6" />
            </svg>
          </button>

          {/* Model Dropdown */}
          {isModelDropdownOpen && (
            <div
              className="absolute bottom-full left-0 mb-1 z-50 min-w-[160px] rounded-lg border shadow-lg"
              style={{
                backgroundColor: "rgb(var(--surface-0))",
                borderColor: "rgb(var(--border-default))",
              }}
              role="listbox"
              aria-label="Available models"
            >
              {AVAILABLE_MODELS.map((model) => (
                <button
                  key={model.id}
                  role="option"
                  aria-selected={model.id === selectedModel}
                  onClick={() => {
                    handleModelSelect(model.id);
                  }}
                  className="w-full px-3 py-2 text-left text-xs transition-colors first:rounded-t-lg last:rounded-b-lg hover:bg-black/5"
                  style={{
                    color:
                      model.id === selectedModel
                        ? "rgb(var(--accent-primary))"
                        : "rgb(var(--text-primary))",
                    fontWeight: model.id === selectedModel ? 600 : 400,
                  }}
                >
                  {model.label}
                  {model.id === selectedModel && (
                    <svg
                      className="inline-block ml-1.5 h-3 w-3"
                      fill="none"
                      viewBox="0 0 24 24"
                      stroke="currentColor"
                      strokeWidth={3}
                      aria-hidden="true"
                    >
                      <path d="M5 13l4 4L19 7" />
                    </svg>
                  )}
                </button>
              ))}
            </div>
          )}
        </div>

        {/* Settings / Params Icon */}
        <div className="relative" ref={paramsDropdownRef}>
          <button
            type="button"
            onClick={() => {
              setIsParamsOpen((prev) => !prev);
              setIsModelDropdownOpen(false);
            }}
            className="flex items-center justify-center rounded-full border p-1 transition-colors hover:bg-black/5 focus:outline-none focus:ring-2"
            style={{
              borderColor: "rgb(var(--border-default))",
              backgroundColor: isParamsOpen ? "rgb(var(--surface-2))" : "rgb(var(--surface-1))",
              color: "rgb(var(--text-muted))",
            }}
            aria-label="Chat parameters"
            aria-expanded={isParamsOpen}
            aria-haspopup="dialog"
            title="Configure temperature and max tokens"
          >
            <svg
              className="h-3.5 w-3.5"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
              strokeWidth={2}
              aria-hidden="true"
            >
              <path d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
            </svg>
          </button>

          {/* Params Popover */}
          {isParamsOpen && (
            <div
              className="absolute bottom-full left-0 mb-1 z-50 w-64 rounded-lg border p-4 shadow-lg"
              style={{
                backgroundColor: "rgb(var(--surface-0))",
                borderColor: "rgb(var(--border-default))",
              }}
              role="dialog"
              aria-label="Chat parameters"
            >
              <h3
                className="mb-3 text-xs font-semibold uppercase tracking-wide"
                style={{ color: "rgb(var(--text-muted))" }}
              >
                Parameters
              </h3>

              {/* Temperature */}
              <div className="mb-4">
                <div className="mb-1.5 flex items-center justify-between">
                  <label
                    className="text-xs font-medium"
                    style={{ color: "rgb(var(--text-secondary))" }}
                    htmlFor="temperature-slider"
                  >
                    Temperature
                  </label>
                  <span
                    className="text-xs font-mono tabular-nums"
                    style={{ color: "rgb(var(--accent-primary))" }}
                  >
                    {temperature.toFixed(1)}
                  </span>
                </div>
                <input
                  id="temperature-slider"
                  type="range"
                  min={0}
                  max={2}
                  step={0.1}
                  value={temperature}
                  onChange={(e) => {
                    handleTemperatureChange(parseFloat(e.target.value));
                  }}
                  className="w-full h-1.5 rounded-full appearance-none cursor-pointer"
                  style={{
                    accentColor: "rgb(var(--accent-primary))",
                    backgroundColor: "rgb(var(--surface-2))",
                  }}
                  aria-label={`Temperature: ${temperature.toFixed(1)}`}
                />
                <div
                  className="mt-1 flex justify-between text-[10px]"
                  style={{ color: "rgb(var(--text-muted))" }}
                >
                  <span>Precise</span>
                  <span>Creative</span>
                </div>
              </div>

              {/* Max Tokens */}
              <div>
                <label
                  className="mb-1.5 block text-xs font-medium"
                  style={{ color: "rgb(var(--text-secondary))" }}
                  htmlFor="max-tokens-input"
                >
                  Max Tokens
                </label>
                <input
                  id="max-tokens-input"
                  type="number"
                  min={100}
                  max={32000}
                  step={100}
                  value={maxTokens}
                  onChange={(e) => {
                    const val = parseInt(e.target.value, 10);
                    if (!isNaN(val) && val >= 100 && val <= 32000) {
                      handleMaxTokensChange(val);
                    }
                  }}
                  className="w-full rounded-md border px-2.5 py-1.5 text-xs outline-none focus:ring-2"
                  style={{
                    backgroundColor: "rgb(var(--surface-1))",
                    borderColor: "rgb(var(--border-default))",
                    color: "rgb(var(--text-primary))",
                  }}
                  aria-label="Maximum tokens"
                />
                <p className="mt-1 text-[10px]" style={{ color: "rgb(var(--text-muted))" }}>
                  100 – 32,000
                </p>
              </div>
            </div>
          )}
        </div>
      </div>

      {/* Command Autocomplete Dropdown */}
      {commandSuggestions.length > 0 && (
        <div
          ref={commandDropdownRef}
          className="rounded-lg border shadow-lg"
          style={{
            backgroundColor: "rgb(var(--surface-0))",
            borderColor: "rgb(var(--border-default))",
          }}
          role="listbox"
          aria-label="Command suggestions"
          data-testid="command-autocomplete"
        >
          {commandSuggestions.map((cmd, idx) => (
            <button
              key={cmd.name}
              role="option"
              aria-selected={idx === highlightedCommandIndex}
              onClick={() => {
                acceptCommand(cmd.name);
              }}
              className="w-full flex items-center gap-3 px-3 py-2 text-left text-sm transition-colors first:rounded-t-lg last:rounded-b-lg"
              style={{
                backgroundColor:
                  idx === highlightedCommandIndex
                    ? "rgb(var(--accent-primary) / 0.1)"
                    : "transparent",
                color: "rgb(var(--text-primary))",
              }}
            >
              <span
                className="font-mono text-xs font-semibold"
                style={{ color: "rgb(var(--accent-primary))" }}
              >
                {cmd.name}
              </span>
              {cmd.aliases && cmd.aliases.length > 0 && (
                <span className="text-xs" style={{ color: "rgb(var(--text-muted))" }}>
                  ({cmd.aliases.join(", ")})
                </span>
              )}
              <span className="text-xs ml-auto" style={{ color: "rgb(var(--text-secondary))" }}>
                {cmd.description}
              </span>
            </button>
          ))}
        </div>
      )}

      {/* Input Container */}
      <div
        className="relative rounded-lg border transition-all duration-150"
        style={{
          backgroundColor: "rgb(var(--surface-0))",
          borderColor:
            isInputDisabled || isStreaming
              ? "rgb(var(--border-default))"
              : "rgb(var(--border-strong))",
        }}
      >
        <textarea
          ref={inputRef}
          value={message}
          onChange={(e) => {
            setMessage(e.target.value);
          }}
          onKeyDown={handleKeyDown}
          placeholder={isStreaming ? "AI is responding..." : "Type a message..."}
          disabled={isInputDisabled || isStreaming}
          rows={1}
          className="block w-full resize-none bg-transparent px-4 py-3 pr-24 text-sm outline-none placeholder:text-[rgb(var(--text-muted))] disabled:opacity-50"
          style={{
            color: "rgb(var(--text-primary))",
            minHeight: "48px",
            maxHeight: "200px",
          }}
          onInput={(e): void => {
            const target = e.target as HTMLTextAreaElement;
            target.style.height = "auto";
            target.style.height = `${String(Math.min(target.scrollHeight, 200))}px`;
          }}
          aria-label="Message input"
          aria-describedby="input-help"
        />

        {/* Send / Stop Button */}
        <div className="absolute bottom-2 right-2 flex items-center gap-2">
          {isStreaming ? (
            <button
              onClick={handleStop}
              className="btn-sm rounded-md flex items-center gap-1.5"
              style={{
                backgroundColor: "rgb(var(--semantic-error))",
                color: "white",
                padding: "0.25rem 0.75rem",
              }}
              aria-label="Stop generating"
              title="Stop generating"
            >
              <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                <rect x="6" y="6" width="12" height="12" rx="1" />
              </svg>
              <span className="hidden sm:inline text-sm font-medium">Stop</span>
            </button>
          ) : (
            <button
              onClick={handleSubmit}
              disabled={isInputDisabled || !message.trim() || isOverLimit}
              className="btn-primary btn-sm rounded-md"
              style={{
                opacity: isInputDisabled || !message.trim() || isOverLimit ? 0.5 : 1,
              }}
              aria-label="Send message"
            >
              <svg
                className="h-4 w-4"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2}
              >
                <path d="M5 12h14M12 5l7 7-7 7" />
              </svg>
              <span className="hidden sm:inline">Send</span>
            </button>
          )}
        </div>
      </div>

      {/* Footer: Keyboard shortcuts + character count */}
      <div
        className="flex items-center justify-between text-xs"
        style={{ color: "rgb(var(--text-muted))" }}
        id="input-help"
      >
        <div className="hidden items-center gap-4 sm:flex">
          <div className="flex items-center gap-1.5">
            <span className="kbd">Enter</span>
            <span>to send</span>
          </div>
          <div className="flex items-center gap-1.5">
            <span className="kbd">Shift</span>
            <span>+</span>
            <span className="kbd">Enter</span>
            <span>for new line</span>
          </div>
        </div>

        <div className="sm:hidden">Tap send or press Enter</div>

        <div
          className="flex items-center gap-2"
          style={{
            color: isOverLimit
              ? "rgb(var(--semantic-error))"
              : isNearLimit
                ? "rgb(var(--semantic-warning))"
                : "rgb(var(--text-muted))",
          }}
        >
          {characterCount > 0 && (
            <>
              <span>
                {characterCount.toLocaleString()}/{maxCharacters.toLocaleString()}
              </span>
              {isOverLimit && (
                <svg
                  className="h-3.5 w-3.5"
                  fill="none"
                  viewBox="0 0 24 24"
                  stroke="currentColor"
                  strokeWidth={2}
                >
                  <circle cx="12" cy="12" r="10" />
                  <line x1="12" y1="8" x2="12" y2="12" />
                  <line x1="12" y1="16" x2="12.01" y2="16" />
                </svg>
              )}
            </>
          )}
        </div>
      </div>

      {/* AI Disclaimer & Version */}
      <div
        className="flex items-center justify-center gap-2 text-xs"
        style={{ color: "rgb(var(--text-muted))" }}
      >
        <span>AI may produce inaccurate information. Verify important details.</span>
        {version && (
          <>
            <span>·</span>
            <span>v{version}</span>
          </>
        )}
      </div>
    </div>
  );
}
