import { AblyCliTerminal, type AblyCliTerminalHandle } from "@ably/react-web-cli";
import { useCallback, useEffect, useRef, useState } from "react";
import { Key, Settings, Shield } from "lucide-react";

import "./App.css";
import { CliDrawer } from "./components/CliDrawer";
import { AuthSettings } from "./components/AuthSettings";
import { AuthScreen } from "./components/AuthScreen";

// Extend Window interface for CLI-specific properties
interface CliWindow extends Window {
  __ABLY_CLI_CI_AUTH_TOKEN__?: string;
  __ABLY_CLI_CI_MODE__?: string;
  _sessionId?: string;
}

// Default WebSocket URL - use public endpoint for production, localhost for development
const DEFAULT_PRODUCTION_WEBSOCKET_URL = "wss://web-cli-terminal.ably.com";
const DEFAULT_DEVELOPMENT_WEBSOCKET_URL = "wss://web-cli-terminal.ably-dev.com";

// Get WebSocket URL from query parameters only
const getWebSocketUrl = () => {
  const urlParams = new URLSearchParams(window.location.search);
  const serverParam = urlParams.get("serverUrl");
  if (serverParam) {
    console.log(`[App.tsx] Found serverUrl param: ${serverParam}`);
    return serverParam;
  }
  return isRunningCIMode() ? DEFAULT_DEVELOPMENT_WEBSOCKET_URL : DEFAULT_PRODUCTION_WEBSOCKET_URL;
};

const isRunningCIMode = (): boolean => {
  return (window as CliWindow).__ABLY_CLI_CI_MODE__ === "true";
}
// Get CI auth token if available
const getCIAuthToken = (): string | undefined => {
  return (window as CliWindow).__ABLY_CLI_CI_AUTH_TOKEN__;
};

// Get signed credentials from various sources
const getInitialCredentials = () => {
  const urlParams = new URLSearchParams(window.location.search);

  // Get the domain from the WebSocket URL for scoping
  const wsUrl = getWebSocketUrl();
  const wsDomain = new URL(wsUrl).host;

  // Check if we should clear credentials (for testing)
  if (urlParams.get('clearCredentials') === 'true') {
    // Clear new signed format
    localStorage.removeItem(`ably.web-cli.signedConfig.${wsDomain}`);
    localStorage.removeItem(`ably.web-cli.signature.${wsDomain}`);
    localStorage.removeItem(`ably.web-cli.rememberCredentials.${wsDomain}`);
    sessionStorage.removeItem(`ably.web-cli.signedConfig.${wsDomain}`);
    sessionStorage.removeItem(`ably.web-cli.signature.${wsDomain}`);
    // Also clear old format (migration)
    localStorage.removeItem(`ably.web-cli.apiKey.${wsDomain}`);
    localStorage.removeItem(`ably.web-cli.accessToken.${wsDomain}`);
    sessionStorage.removeItem(`ably.web-cli.apiKey.${wsDomain}`);
    sessionStorage.removeItem(`ably.web-cli.accessToken.${wsDomain}`);
    // Remove the clearCredentials param from URL
    const cleanUrl = new URL(window.location.href);
    cleanUrl.searchParams.delete('clearCredentials');
    window.history.replaceState(null, '', cleanUrl.toString());
  }

  // Check query parameters (only in development/test environments)
  const qsSignedConfig = urlParams.get('signedConfig');
  const qsSignature = urlParams.get('signature');

  if (qsSignedConfig && qsSignature) {
    // Security check: only allow query param auth in development/test environments
    const isProduction = import.meta.env.PROD &&
                        !window.location.hostname.includes('localhost') &&
                        !window.location.hostname.includes('127.0.0.1');

    if (isProduction) {
      console.error('[App] Security Warning: Signed credentials in query parameters are not allowed in production.');
      console.error('[App] Credentials contain API keys that can leak through browser history, server logs, and shared URLs.');
      // Clear the sensitive query parameters from the URL
      const cleanUrl = new URL(window.location.href);
      cleanUrl.searchParams.delete('signedConfig');
      cleanUrl.searchParams.delete('signature');
      cleanUrl.searchParams.delete('clearCredentials');
      window.history.replaceState(null, '', cleanUrl.toString());
      // Don't use these credentials - fall through to storage check
    } else {
      console.log('[App] Using signed config from query parameters (dev/test mode)');
      return {
        signedConfig: qsSignedConfig,
        signature: qsSignature,
        source: 'query' as const
      };
    }
  }

  // Check localStorage for persisted signed credentials (if user chose to remember)
  const rememberCredentials = localStorage.getItem(`ably.web-cli.rememberCredentials.${wsDomain}`) === 'true';
  if (rememberCredentials) {
    const storedSignedConfig = localStorage.getItem(`ably.web-cli.signedConfig.${wsDomain}`);
    const storedSignature = localStorage.getItem(`ably.web-cli.signature.${wsDomain}`);
    if (storedSignedConfig && storedSignature) {
      console.log('[App] Using signed config from localStorage');
      return {
        signedConfig: storedSignedConfig,
        signature: storedSignature,
        source: 'localStorage' as const
      };
    }
  }

  // Check sessionStorage for session-only signed credentials
  const sessionSignedConfig = sessionStorage.getItem(`ably.web-cli.signedConfig.${wsDomain}`);
  const sessionSignature = sessionStorage.getItem(`ably.web-cli.signature.${wsDomain}`);
  if (sessionSignedConfig && sessionSignature) {
    console.log('[App] Using signed config from sessionStorage');
    return {
      signedConfig: sessionSignedConfig,
      signature: sessionSignature,
      source: 'session' as const
    };
  }

  // Check for old format credentials (migration)
  const oldApiKey = localStorage.getItem(`ably.web-cli.apiKey.${wsDomain}`) ||
                    sessionStorage.getItem(`ably.web-cli.apiKey.${wsDomain}`);
  if (oldApiKey) {
    console.warn('[App] Found old credential format. Please re-authenticate with signed credentials.');
    // Clear old format
    localStorage.removeItem(`ably.web-cli.apiKey.${wsDomain}`);
    localStorage.removeItem(`ably.web-cli.accessToken.${wsDomain}`);
    sessionStorage.removeItem(`ably.web-cli.apiKey.${wsDomain}`);
    sessionStorage.removeItem(`ably.web-cli.accessToken.${wsDomain}`);
  }

  return { signedConfig: undefined, signature: undefined, source: 'none' as const };
};

function App() {
  // Read initial mode from URL, default to fullscreen
  const initialMode = new URLSearchParams(window.location.search).get("mode") as ("fullscreen" | "drawer") || "fullscreen";

  type TermStatus = 'initial' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'error';
  const [connectionStatus, setConnectionStatus] = useState<TermStatus>('disconnected');
  const [displayMode, setDisplayMode] = useState<"fullscreen" | "drawer">(initialMode);
  const [showAuthSettings, setShowAuthSettings] = useState(false);

  // Initialize signed credentials
  const initialCreds = getInitialCredentials();
  const [signedConfig, setSignedConfig] = useState<string | undefined>(initialCreds.signedConfig);
  const [signature, setSignature] = useState<string | undefined>(initialCreds.signature);
  const [isAuthenticated, setIsAuthenticated] = useState(Boolean(initialCreds.signedConfig && initialCreds.signature));
  const [authSource, setAuthSource] = useState(initialCreds.source);
  // Get the URL and domain early for use in state initialization
  const currentWebsocketUrl = getWebSocketUrl();
  const wsDomain = new URL(currentWebsocketUrl).host;
  const [rememberCredentials, setRememberCredentials] = useState(localStorage.getItem(`ably.web-cli.rememberCredentials.${wsDomain}`) === 'true');

  // Store the latest sessionId globally for E2E tests / debugging
  const handleSessionId = useCallback((id: string) => {
    console.log(`[App] Received sessionId: ${id}`);
    (window as CliWindow)._sessionId = id; // Expose for Playwright
  }, []);

  const handleConnectionChange = useCallback((status: TermStatus) => {
    console.log("Connection Status:", status);
    setConnectionStatus(status);
  }, []);

  const handleSessionEnd = useCallback((reason: string) => {
    console.log("Session ended:", reason);
  }, []);

  // Handle authentication
  const handleAuthenticate = useCallback(async (newApiKey: string, remember?: boolean) => {
    try {
      // Call /api/sign endpoint to get signed config
      const response = await fetch('/api/sign', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          apiKey: newApiKey,
          bypassRateLimit: false
        })
      });

      if (!response.ok) {
        const error = await response.json();
        console.error('[App] Failed to sign credentials:', error);
        throw new Error(error.error || 'Failed to sign credentials');
      }

      const { signedConfig: newSignedConfig, signature: newSignature } = await response.json();

      // Clear any existing session data when credentials change (domain-scoped)
      sessionStorage.removeItem(`ably.cli.sessionId.${wsDomain}`);
      sessionStorage.removeItem(`ably.cli.secondarySessionId.${wsDomain}`);
      sessionStorage.removeItem(`ably.cli.isSplit.${wsDomain}`);

      setSignedConfig(newSignedConfig);
      setSignature(newSignature);
      setIsAuthenticated(true);
      setShowAuthSettings(false);

      // Determine if we should remember based on parameter or current state
      const shouldRemember = remember !== undefined ? remember : rememberCredentials;

      if (shouldRemember) {
        // Store in localStorage for persistence (domain-scoped)
        localStorage.setItem(`ably.web-cli.signedConfig.${wsDomain}`, newSignedConfig);
        localStorage.setItem(`ably.web-cli.signature.${wsDomain}`, newSignature);
        localStorage.setItem(`ably.web-cli.rememberCredentials.${wsDomain}`, 'true');
        setAuthSource('localStorage');
      } else {
        // Store only in sessionStorage (domain-scoped)
        sessionStorage.setItem(`ably.web-cli.signedConfig.${wsDomain}`, newSignedConfig);
        sessionStorage.setItem(`ably.web-cli.signature.${wsDomain}`, newSignature);
        // Clear from localStorage if it was there (domain-scoped)
        localStorage.removeItem(`ably.web-cli.signedConfig.${wsDomain}`);
        localStorage.removeItem(`ably.web-cli.signature.${wsDomain}`);
        localStorage.removeItem(`ably.web-cli.rememberCredentials.${wsDomain}`);
        setAuthSource('session');
      }

      setRememberCredentials(shouldRemember);
    } catch (error) {
      console.error('[App] Authentication error:', error);
      throw error;
    }
  }, [rememberCredentials, wsDomain]);

  // Handle auth settings save
  const handleAuthSettingsSave = useCallback(async (newApiKey: string, remember: boolean) => {
    if (newApiKey) {
      await handleAuthenticate(newApiKey, remember);
    } else {
      // Clear all credentials - go back to auth screen (domain-scoped)
      sessionStorage.removeItem(`ably.cli.sessionId.${wsDomain}`);
      sessionStorage.removeItem(`ably.cli.secondarySessionId.${wsDomain}`);
      sessionStorage.removeItem(`ably.cli.isSplit.${wsDomain}`);
      sessionStorage.removeItem(`ably.web-cli.signedConfig.${wsDomain}`);
      sessionStorage.removeItem(`ably.web-cli.signature.${wsDomain}`);
      localStorage.removeItem(`ably.web-cli.signedConfig.${wsDomain}`);
      localStorage.removeItem(`ably.web-cli.signature.${wsDomain}`);
      localStorage.removeItem(`ably.web-cli.rememberCredentials.${wsDomain}`);
      setSignedConfig(undefined);
      setSignature(undefined);
      setIsAuthenticated(false);
      setShowAuthSettings(false);
      setRememberCredentials(false);
    }
  }, [handleAuthenticate, wsDomain]);

  // Effect to update URL when displayMode changes
  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get("mode") !== displayMode) {
      urlParams.set("mode", displayMode);
      window.history.replaceState({}, '', `${window.location.pathname}?${urlParams.toString()}`);
    }
  }, [displayMode]);

  // Prepare the terminal component instance to pass it down
  const termRef = useRef<AblyCliTerminalHandle>(null);
  const TerminalInstance = useCallback(() => (
    isAuthenticated && signedConfig && signature ? (
      <AblyCliTerminal
        ref={termRef}
        signedConfig={signedConfig}
        signature={signature}
        onConnectionStatusChange={handleConnectionChange}
        onSessionEnd={handleSessionEnd}
        onSessionId={handleSessionId}
        websocketUrl={currentWebsocketUrl}
        resumeOnReload={true}
        enableSplitScreen={true}
        showSplitControl={true}
        maxReconnectAttempts={5} /* In the example, limit reconnection attempts for testing, default is 15 */
      />
    ) : null
  ), [isAuthenticated, signedConfig, signature, handleConnectionChange, handleSessionEnd, handleSessionId, currentWebsocketUrl]);

  // Show auth screen if not authenticated
  if (!isAuthenticated) {
    return <AuthScreen
      onAuthenticate={handleAuthenticate}
      rememberCredentials={rememberCredentials}
      onRememberChange={setRememberCredentials}
    />;
  }

  return (
    <div className="App fixed">
      {/* Updated header with auth button */}
      <header className="App-header">
        <span className="font-semibold text-base">Ably Web CLI Terminal</span>
        <div className="header-info">
          <span>Status: <span className={`status status-${connectionStatus}`}>{connectionStatus}</span></span>
          <span>Server: {currentWebsocketUrl}</span>
          <button
            onClick={() => termRef.current?.toggleSplitScreen()}
            className="auth-button flex items-center space-x-2 px-3 py-1.5 bg-gray-800 hover:bg-gray-700 rounded-md transition-colors"
            title="Toggle Split Screen"
          >
            Toggle Split
          </button>
          <button
            onClick={() => setShowAuthSettings(true)}
            className="auth-button flex items-center space-x-2 px-3 py-1.5 bg-gray-800 hover:bg-gray-700 rounded-md transition-colors"
            title="Authentication Settings"
          >
            {authSource === 'session' ? (
              <>
                <Key size={16} />
                <span className="text-sm">Session Auth</span>
              </>
            ) : authSource === 'localStorage' ? (
              <>
                <Shield size={16} className="text-green-500" />
                <span className="text-sm">Saved Auth</span>
              </>
            ) : authSource === 'query' ? (
              <>
                <Key size={16} className="text-blue-500" />
                <span className="text-sm">Query Params</span>
              </>
            ) : (
              <>
                <Shield size={16} />
                <span className="text-sm">Auth</span>
              </>
            )}
            <Settings size={14} className="ml-1 opacity-50" />
          </button>
        </div>
        <div className="toggle-group">
          <button
            className={`toggle-segment ${displayMode === 'fullscreen' ? 'active' : ''}`}
            onClick={() => setDisplayMode('fullscreen')}
          >
            Fullscreen
          </button>
          <button
            className={`toggle-segment ${displayMode === 'drawer' ? 'active' : ''}`}
            onClick={() => setDisplayMode('drawer')}
          >
            Drawer
          </button>
        </div>
      </header>

      {/* Main content */}
      {displayMode === 'fullscreen' ? (
        <main className="App-main no-padding">
          <div className="Terminal-container">
            <TerminalInstance />
          </div>
        </main>
      ) : (
        <CliDrawer TerminalComponent={TerminalInstance} />
      )}

      {/* Auth settings modal */}
      <AuthSettings
        isOpen={showAuthSettings}
        onClose={() => setShowAuthSettings(false)}
        onSave={handleAuthSettingsSave}
        currentSignedConfig={signedConfig}
        rememberCredentials={rememberCredentials}
      />
    </div>
  );
}

export default App;
