import { type JSX, type ReactNode, useCallback, useRef, useState } from "react";
import { Button } from "../ui/button";
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
import {
  Eye,
  EyeOff,
  Pencil,
  Trash2,
  RotateCw,
  CheckCircle,
  Minus,
  ExternalLink,
  AlertCircle,
  Wifi,
  WifiOff,
  Lock,
  Gauge,
  Server,
  HelpCircle,
  Clock,
  Copy,
  Check,
} from "lucide-react";
import type { ProviderConfig } from "../../proxy/providers";
import { maskApiKey } from "../../lib/mask";
import type {
  ProviderTestErrorType as ErrorType,
  ProviderTestResult as TestResult,
  ProviderTestResults as TestResults,
  ProviderTestState,
} from "../../lib/providerTestContract";

// Known provider documentation links (fallback when apiDocsUrl is not configured)
const KNOWN_PROVIDER_DOCS: Record<string, string> = {
  deepseek: "https://api-docs.deepseek.com/zh-cn/",
};

type ProviderCardProps = {
  provider: ProviderConfig;
  testResults?: TestResults;
  isTesting?: boolean;
  testingTimeLeft?: number;
  isHighlighted?: boolean;
  onEdit: (provider: ProviderConfig) => void;
  onDelete: (providerId: string) => void;
  onTest?: (providerId: string) => void;
};

function hasSuccessField(result: ProviderTestState): result is TestResult {
  return Object.prototype.hasOwnProperty.call(result, "success");
}

// Using Object.prototype.hasOwnProperty.call to avoid 'in' operator
function isNotConfiguredState(result: ProviderTestState): result is { notConfigured: true } {
  return Object.prototype.hasOwnProperty.call(result, "notConfigured");
}

function getErrorIcon(type: ErrorType): JSX.Element {
  const iconProps = { className: "size-3", strokeWidth: 2 };
  switch (type) {
    case "timeout":
      return <Clock {...iconProps} />;
    case "network_unreachable":
      return <WifiOff {...iconProps} />;
    case "connection_refused":
      return <Wifi {...iconProps} />;
    case "auth_failed":
      return <Lock {...iconProps} />;
    case "rate_limited":
      return <Gauge {...iconProps} />;
    case "server_error":
      return <Server {...iconProps} />;
    case "invalid_response":
      return <HelpCircle {...iconProps} />;
    case "unknown":
      return <AlertCircle {...iconProps} />;
    default:
      return <AlertCircle {...iconProps} />;
  }
}

function TestStatus({ result }: { result: ProviderTestState }): JSX.Element {
  const [showDetails, setShowDetails] = useState(false);

  if (!hasSuccessField(result)) {
    // Not TestResult - check if NotConfigured or Testing
    if (isNotConfiguredState(result)) {
      return (
        <div className="flex items-center gap-1 text-xs text-muted-foreground shrink-0">
          <Minus className="size-3" />
          <span>Not configured</span>
        </div>
      );
    }
    // Must be Testing state
    return (
      <div className="flex items-center gap-1 text-xs text-muted-foreground shrink-0">
        <RotateCw className="size-3 animate-spin" />
        <span>Testing...</span>
      </div>
    );
  }
  if (result.success) {
    const tokenParts: ReactNode[] = [];
    if (result.inputTokens !== undefined) {
      tokenParts.push(<span key="in">{result.inputTokens} in</span>);
    }
    if (result.outputTokens !== undefined) {
      tokenParts.push(<span key="out">{result.outputTokens} out</span>);
    }
    if (result.cacheCreationInputTokens !== undefined && result.cacheCreationInputTokens > 0) {
      tokenParts.push(
        <TooltipProvider key="cache-create">
          <Tooltip>
            <TooltipTrigger asChild>
              <span className="font-mono tabular-nums text-emerald-400">
                +{result.cacheCreationInputTokens} cache
              </span>
            </TooltipTrigger>
            <TooltipContent>Tokens cached for reuse, reducing future API cost</TooltipContent>
          </Tooltip>
        </TooltipProvider>,
      );
    }
    if (result.cacheReadInputTokens !== undefined && result.cacheReadInputTokens > 0) {
      tokenParts.push(
        <TooltipProvider key="cache-read">
          <Tooltip>
            <TooltipTrigger asChild>
              <span className="font-mono tabular-nums text-purple-400">
                ~{result.cacheReadInputTokens} cached
              </span>
            </TooltipTrigger>
            <TooltipContent>Tokens served from cache, reducing API cost</TooltipContent>
          </Tooltip>
        </TooltipProvider>,
      );
    }
    const displayTokens: ReactNode[] = [];
    for (let i = 0; i < tokenParts.length; i++) {
      if (i > 0) displayTokens.push(", ");
      displayTokens.push(tokenParts[i]);
    }
    return (
      <TooltipProvider>
        <Tooltip>
          <TooltipTrigger asChild>
            <div className="flex items-center gap-1 text-xs text-green-600 shrink-0">
              <CheckCircle className="size-3" />
              <span>Connected</span>
              {tokenParts.length > 0 && (
                <span className="text-muted-foreground">({displayTokens})</span>
              )}
            </div>
          </TooltipTrigger>
          <TooltipContent>Connection test passed</TooltipContent>
        </Tooltip>
      </TooltipProvider>
    );
  }

  const error = result.error;
  const errorMessage = error?.message ?? "Connection failed";
  const errorHint = error?.hint;
  const errorDetails = error?.details;
  const errorType = error?.type ?? "unknown";

  return (
    <div className="flex flex-col gap-1 shrink-0">
      <div className="flex items-center gap-1 text-xs text-red-600 max-w-[200px]">
        {getErrorIcon(errorType)}
        <TooltipProvider>
          <Tooltip>
            <TooltipTrigger asChild>
              <span className="truncate">{errorMessage}</span>
            </TooltipTrigger>
            <TooltipContent>Connection test failed</TooltipContent>
          </Tooltip>
        </TooltipProvider>
        {errorDetails !== undefined && (
          <TooltipProvider>
            <Tooltip>
              <TooltipTrigger asChild>
                <button
                  type="button"
                  onClick={() => setShowDetails(!showDetails)}
                  className="shrink-0 text-muted-foreground hover:text-foreground transition-colors"
                >
                  {showDetails ? <EyeOff className="size-3" /> : <Eye className="size-3" />}
                </button>
              </TooltipTrigger>
              <TooltipContent>
                {showDetails ? "Hide error details" : "Show detailed error information"}
              </TooltipContent>
            </Tooltip>
          </TooltipProvider>
        )}
      </div>
      {showDetails && errorDetails !== undefined && (
        <div className="text-xs text-muted-foreground bg-muted/50 rounded p-2 max-w-[300px]">
          {errorHint !== undefined && <div className="mb-1">{errorHint}</div>}
          <div className="font-mono whitespace-pre-wrap break-all text-red-400/80">
            {errorDetails}
          </div>
        </div>
      )}
    </div>
  );
}

function formatTimeAgo(isoString: string): string {
  const now = Date.now();
  const then = new Date(isoString).getTime();
  const diffMs = now - then;
  const diffSec = Math.floor(diffMs / 1000);
  if (diffSec < 60) return "just now";
  const diffMin = Math.floor(diffSec / 60);
  if (diffMin < 60) return `${diffMin}m ago`;
  const diffHr = Math.floor(diffMin / 60);
  if (diffHr < 24) return `${diffHr}h ago`;
  const diffDay = Math.floor(diffHr / 24);
  return `${diffDay}d ago`;
}

export function ProviderCard({
  provider,
  testResults,
  isTesting,
  testingTimeLeft,
  isHighlighted,
  onEdit,
  onDelete,
  onTest,
}: ProviderCardProps): JSX.Element {
  const [showApiKey, setShowApiKey] = useState(false);
  const [copied, setCopied] = useState(false);
  const [showModelResults, setShowModelResults] = useState(false);
  const hasLoggedRef = useRef<string | null>(null);
  const lastTestedAtRef = useRef<string | undefined>(undefined);

  // Reset log state when new test results arrive
  if (testResults?.testedAt !== undefined && testResults.testedAt !== lastTestedAtRef.current) {
    lastTestedAtRef.current = testResults.testedAt;
    hasLoggedRef.current = null;
  }

  // Call log API when user expands model test results for the first time
  const handleToggleModelResults = useCallback(() => {
    setShowModelResults((v) => {
      const next = !v;
      if (next && hasLoggedRef.current === null && testResults?.models !== undefined) {
        hasLoggedRef.current = testResults.testedAt ?? "";
        // Fire-and-forget: commit test results to dashboard log
        void fetch(`/api/providers/${provider.id}/test/log`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(testResults),
        });
      }
      return next;
    });
  }, [provider.id, testResults]);

  function handleCopy() {
    navigator.clipboard.writeText(provider.apiKey).catch(() => {});
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  }

  // Get docs URL: use provider.apiDocsUrl if configured, otherwise check KNOWN_PROVIDER_DOCS
  const docsUrl =
    provider.apiDocsUrl ??
    Object.entries(KNOWN_PROVIDER_DOCS).find(([keyword]) =>
      provider.name.toLowerCase().includes(keyword),
    )?.[1];

  return (
    <div
      className={`border rounded-lg p-4 flex flex-col gap-3 bg-card transition-all duration-500 ${isHighlighted === true ? "ring-2 ring-primary shadow-md" : ""}`}
    >
      <div className="flex items-start justify-between gap-2">
        <div className="flex items-center gap-2 min-w-0">
          <span className="font-medium truncate">{provider.name}</span>
          {provider.source === "company" && (
            <TooltipProvider>
              <Tooltip>
                <TooltipTrigger asChild>
                  <span className="text-xs px-1.5 py-0.5 rounded bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400 shrink-0">
                    公司
                  </span>
                </TooltipTrigger>
                <TooltipContent>Company-provided API key</TooltipContent>
              </Tooltip>
            </TooltipProvider>
          )}
          {provider.source === "personal" && (
            <TooltipProvider>
              <Tooltip>
                <TooltipTrigger asChild>
                  <span className="text-xs px-1.5 py-0.5 rounded bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 shrink-0">
                    个人
                  </span>
                </TooltipTrigger>
                <TooltipContent>Your personal API key</TooltipContent>
              </Tooltip>
            </TooltipProvider>
          )}
        </div>
        {docsUrl !== undefined && (
          <a
            href={docsUrl}
            target="_blank"
            rel="noopener noreferrer"
            className="text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1 text-xs"
            title="View API documentation"
          >
            <ExternalLink className="size-3" />
            <span className="sr-only">Docs</span>
          </a>
        )}
      </div>

      {provider.models !== undefined && provider.models.length > 0 && (
        <div className="flex flex-wrap gap-1">
          {provider.models.map((m) => (
            <span key={m} className="text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground">
              {m}
            </span>
          ))}
        </div>
      )}

      <div className="flex items-center gap-2">
        <code className="text-xs text-muted-foreground bg-muted px-2 py-1 rounded flex-1 truncate">
          {showApiKey ? provider.apiKey : maskApiKey(provider.apiKey)}
        </code>
        <button
          type="button"
          onClick={() => setShowApiKey((s) => !s)}
          className="text-muted-foreground hover:text-foreground transition-colors p-1"
          aria-label={showApiKey ? "Hide API key" : "Show API key"}
        >
          {showApiKey ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
        </button>
        <button
          type="button"
          onClick={handleCopy}
          className="text-muted-foreground hover:text-foreground transition-colors p-1"
          aria-label="Copy API key"
        >
          {copied ? <Check className="size-4 text-green-500" /> : <Copy className="size-4" />}
        </button>
      </div>

      {provider.anthropicBaseUrl !== undefined && provider.anthropicBaseUrl !== "" && (
        <div className="flex items-center justify-between gap-2">
          <div className="text-xs text-muted-foreground min-w-0 flex-1">
            <span className="font-medium">Anthropic:</span>{" "}
            <span className="truncate">{provider.anthropicBaseUrl}</span>
          </div>
          {testResults && <TestStatus result={testResults.anthropic.nonStreaming} />}
        </div>
      )}

      {provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl !== "" && (
        <div className="flex items-center justify-between gap-2">
          <div className="text-xs text-muted-foreground min-w-0 flex-1">
            <span className="font-medium">OpenAI:</span>{" "}
            <span className="truncate">{provider.openaiBaseUrl}</span>
          </div>
          {testResults && <TestStatus result={testResults.openai.nonStreaming} />}
        </div>
      )}

      {testResults?.testedAt !== undefined && (
        <div className="text-xs text-muted-foreground flex items-center gap-1">
          <Clock className="size-3" />
          <span>Tested {formatTimeAgo(testResults.testedAt)}</span>
        </div>
      )}
      {testResults?.models !== undefined && Object.keys(testResults.models).length > 0 && (
        <div className="border-t pt-2">
          <button
            type="button"
            onClick={handleToggleModelResults}
            className="text-xs text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1"
          >
            <span className="font-mono">{showModelResults ? "▾" : "▸"}</span>
            Model Test Results ({Object.keys(testResults.models).length})
          </button>
          {showModelResults && (
            <div className="mt-2 overflow-x-auto">
              <table className="w-full text-xs border-collapse">
                <thead>
                  <tr className="border-b border-border">
                    <th className="text-left py-1 px-2 font-medium text-muted-foreground">Model</th>
                    {provider.anthropicBaseUrl !== undefined &&
                      provider.anthropicBaseUrl !== "" && (
                        <th className="text-left py-1 px-2 font-medium text-muted-foreground">
                          Anthropic
                        </th>
                      )}
                    {provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl !== "" && (
                      <th className="text-left py-1 px-2 font-medium text-muted-foreground">
                        OpenAI
                      </th>
                    )}
                  </tr>
                </thead>
                <tbody>
                  {Object.entries(testResults.models).map(([modelName, modelResult]) => (
                    <tr key={modelName} className="border-b border-border/50 last:border-0">
                      <td className="py-1 px-2 font-medium">{modelName}</td>
                      {provider.anthropicBaseUrl !== undefined &&
                        provider.anthropicBaseUrl !== "" && (
                          <td className="py-1 px-2">
                            <TestStatus result={modelResult.anthropic.nonStreaming} />
                          </td>
                        )}
                      {provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl !== "" && (
                        <td className="py-1 px-2">
                          <TestStatus result={modelResult.openai.nonStreaming} />
                        </td>
                      )}
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}

      <div className="flex gap-2 pt-1 border-t">
        {onTest !== undefined && (
          <Button
            variant="outline"
            size="sm"
            onClick={() => onTest(provider.id)}
            className="text-xs h-7 gap-1"
            disabled={isTesting ?? false}
          >
            <RotateCw className={`size-3 ${(isTesting ?? false) ? "animate-spin" : ""}`} />
            {(isTesting ?? false)
              ? testingTimeLeft !== undefined
                ? `Testing (${testingTimeLeft}s)`
                : "Testing..."
              : "Test"}
          </Button>
        )}
        <Button
          variant="outline"
          size="sm"
          onClick={() => onEdit(provider)}
          className="text-xs h-7 gap-1"
          disabled={isTesting ?? false}
        >
          <Pencil className="size-3" />
          Edit
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => onDelete(provider.id)}
          className="text-xs h-7 gap-1 text-destructive hover:text-destructive"
          disabled={isTesting ?? false}
        >
          <Trash2 className="size-3" />
          Delete
        </Button>
      </div>
    </div>
  );
}
