"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import PropTypes from "prop-types";
import { Modal, Button, Input } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";

/**
 * OAuth Modal Component
 * - Localhost: Auto callback via popup message
 * - Remote: Manual paste callback URL
 */
export default function OAuthModal({
  isOpen,
  provider,
  providerInfo,
  onSuccess,
  onClose,
  idcConfig,
}: any) {
  const [step, setStep] = useState("waiting"); // waiting | input | success | error | method-select | cookie-input
  const [authData, setAuthData] = useState(null);
  const [callbackUrl, setCallbackUrl] = useState("");
  const [error, setError] = useState(null);
  const [isDeviceCode, setIsDeviceCode] = useState(false);
  const [deviceData, setDeviceData] = useState(null);
  const [polling, setPolling] = useState(false);
  const [authMethod, setAuthMethod] = useState(null); // 'oauth' | 'cookie'
  const [cookieValue, setCookieValue] = useState("");
  const [cookieAuthenticating, setCookieAuthenticating] = useState(false);
  const popupRef = useRef(null);
  const { copied, copy } = useCopyToClipboard();

  // State for client-only values to avoid hydration mismatch
  const [isLocalhost, setIsLocalhost] = useState(false);
  const [placeholderUrl, setPlaceholderUrl] = useState("/callback?code=...");
  const callbackProcessedRef = useRef(false);
  const flowStartedRef = useRef(false);

  // Detect if running on localhost (client-side only)
  useEffect(() => {
    if (typeof window !== "undefined") {
      setIsLocalhost(
        window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"
      );
      setPlaceholderUrl(`${window.location.origin}/callback?code=...`);
    }
  }, []);

  // Define all useCallback hooks BEFORE the useEffects that reference them

  // Exchange tokens
  const exchangeTokens = useCallback(
    async (code, state) => {
      if (!authData) return;
      try {
        const res = await fetch(`/api/oauth/${provider}/exchange`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            code,
            redirectUri: authData.redirectUri,
            codeVerifier: authData.codeVerifier,
            state,
          }),
        });

        const data = await res.json();
        if (!res.ok) throw new Error(data.error);

        setStep("success");
        onSuccess?.();
      } catch (err) {
        setError(err.message);
        setStep("error");
      }
    },
    [authData, provider, onSuccess]
  );

  // Poll for device code token
  const startPolling = useCallback(
    async (deviceCode, codeVerifier, interval, extraData) => {
      setPolling(true);
      const maxAttempts = 60;

      for (let i = 0; i < maxAttempts; i++) {
        await new Promise((r) => setTimeout(r, interval * 1000));

        try {
          const res = await fetch(`/api/oauth/${provider}/poll`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ deviceCode, codeVerifier, extraData }),
          });

          const data = await res.json();

          if (data.success) {
            setStep("success");
            setPolling(false);
            onSuccess?.();
            return;
          }

          if (data.error === "expired_token" || data.error === "access_denied") {
            throw new Error(data.errorDescription || data.error);
          }

          if (data.error === "slow_down") {
            interval = Math.min(interval + 5, 30);
          }
        } catch (err) {
          setError(err.message);
          setStep("error");
          setPolling(false);
          return;
        }
      }

      setError("Authorization timeout");
      setStep("error");
      setPolling(false);
    },
    [provider, onSuccess]
  );

  // Start OAuth flow
  const startOAuthFlow = useCallback(async () => {
    if (!provider) return;
    try {
      setError(null);

      // For iFlow, show method selection first
      if (provider === "iflow" && !authMethod) {
        setStep("method-select");
        return;
      }

      // Device code flow (GitHub, Qwen, Kiro, Kimi Coding, KiloCode)
      if (
        provider === "github" ||
        provider === "qwen" ||
        provider === "kiro" ||
        provider === "kimi-coding" ||
        provider === "kilocode"
      ) {
        setIsDeviceCode(true);
        setStep("waiting");

        const res = await fetch(`/api/oauth/${provider}/device-code`);
        const data = await res.json();
        if (!res.ok) throw new Error(data.error);

        setDeviceData(data);

        // Open verification URL
        const verifyUrl = data.verification_uri_complete || data.verification_uri;
        if (verifyUrl) window.open(verifyUrl, "oauth_verify");

        // Start polling - pass extraData for Kiro (contains _clientId, _clientSecret)
        const extraData =
          provider === "kiro"
            ? { _clientId: data._clientId, _clientSecret: data._clientSecret }
            : null;
        startPolling(data.device_code, data.codeVerifier, data.interval || 5, extraData);
        return;
      }

      // Codex: on localhost use callback server on port 1455,
      // on remote use standard auth code flow (callback server is unreachable)
      if (provider === "codex") {
        if (isLocalhost) {
          // Localhost: use callback server on port 1455 + polling
          try {
            const serverRes = await fetch(`/api/oauth/codex/start-callback-server`);
            const serverData = await serverRes.json();
            if (!serverRes.ok) throw new Error(serverData.error);

            setAuthData({ ...serverData, redirectUri: serverData.redirectUri });
            setStep("waiting");
            window.open(serverData.authUrl, "oauth_auth");

            setPolling(true);
            const maxAttempts = 150;
            for (let i = 0; i < maxAttempts; i++) {
              await new Promise((r) => setTimeout(r, 2000));

              const pollRes = await fetch(`/api/oauth/codex/poll-callback`, {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({}),
              });
              const pollData = await pollRes.json();

              if (pollData.success) {
                setStep("success");
                setPolling(false);
                onSuccess?.();
                return;
              }

              if (pollData.error && !pollData.pending) {
                throw new Error(pollData.errorDescription || pollData.error);
              }
            }

            setPolling(false);
            throw new Error("Authorization timeout");
          } catch (codexErr) {
            setPolling(false);
            setStep("input");
            setError(codexErr.message + " — You can paste the callback URL manually below.");
          }
          return;
        }
        // Remote: fall through to standard auth code flow below
      }

      // Authorization code flow
      // Always use localhost redirect_uri — this is what providers have registered.
      // On remote, the browser redirects to localhost (error page), user copies URL and pastes back.
      // Codex (OpenAI) requires exactly http://localhost:1455/auth/callback — the registered URI.
      // Other providers (Antigravity/Gemini via Google OAuth) accept any localhost port.
      let redirectUri: string;
      if (provider === "codex" || provider === "openai") {
        redirectUri = "http://localhost:1455/auth/callback";
      } else if (provider === "iflow") {
        redirectUri = "http://localhost:8080/callback";
      } else {
        const port = window.location.port || (window.location.protocol === "https:" ? "443" : "80");
        redirectUri = `http://localhost:${port}/callback`;
      }

      const res = await fetch(
        `/api/oauth/${provider}/authorize?redirect_uri=${encodeURIComponent(redirectUri)}`
      );
      const data = await res.json();
      if (!res.ok) throw new Error(data.error);

      setAuthData({ ...data, redirectUri });

      // iFlow always uses manual input mode (displays code on their page)
      // For non-localhost: use manual input mode (user pastes callback URL)
      if (provider === "iflow" || !isLocalhost) {
        setStep("input");
        window.open(data.authUrl, "oauth_auth");
      } else {
        // Localhost: Open popup and wait for message
        setStep("waiting");
        popupRef.current = window.open(data.authUrl, "oauth_popup", "width=600,height=700");

        // Check if popup was blocked
        if (!popupRef.current) {
          setStep("input");
        }
      }
    } catch (err) {
      setError(err.message);
      setStep("error");
    }
  }, [provider, isLocalhost, startPolling, onSuccess, authMethod]);

  // Handle cookie authentication for iFlow
  const authenticateWithCookie = useCallback(async () => {
    if (!cookieValue.trim()) {
      setError("Please enter your cookie");
      return;
    }

    setCookieAuthenticating(true);
    setError(null);

    try {
      const res = await fetch("/api/oauth/iflow/cookie", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ cookie: cookieValue }),
      });

      const data = await res.json();

      if (!res.ok) {
        throw new Error(data.error || "Cookie authentication failed");
      }

      setStep("success");
      onSuccess?.();
    } catch (err) {
      setError(err.message);
      setStep("error");
    } finally {
      setCookieAuthenticating(false);
    }
  }, [cookieValue, onSuccess]);

  // Reset guard when modal closes
  useEffect(() => {
    if (!isOpen) {
      flowStartedRef.current = false;
    }
  }, [isOpen]);

  // Reset state and start OAuth when modal opens
  useEffect(() => {
    if (isOpen && provider) {
      if (flowStartedRef.current) return; // Already started, prevent duplicate
      flowStartedRef.current = true;
      setAuthData(null);
      setCallbackUrl("");
      setError(null);
      setIsDeviceCode(false);
      setDeviceData(null);
      setPolling(false);
      setAuthMethod(null);
      setCookieValue("");
      setCookieAuthenticating(false);
      // Auto start OAuth
      startOAuthFlow();
    }
  }, [isOpen, provider, startOAuthFlow]);

  // Listen for OAuth callback via multiple methods
  useEffect(() => {
    if (!authData) return;
    callbackProcessedRef.current = false; // Reset when authData changes

    // Handler for callback data - only process once
    const handleCallback = async (data) => {
      if (callbackProcessedRef.current) return; // Already processed

      const { code, state, error: callbackError, errorDescription } = data;

      if (callbackError) {
        callbackProcessedRef.current = true;
        setError(errorDescription || callbackError);
        setStep("error");
        return;
      }

      if (code) {
        callbackProcessedRef.current = true;
        await exchangeTokens(code, state);
      }
    };

    // Method 1: postMessage from popup
    const handleMessage = (event) => {
      // Accept same-origin OR localhost with same port (remote access scenario:
      // dashboard at 192.168.x:port, callback redirects to localhost:port)
      const currentPort = window.location.port;
      const isLocalhostSamePort =
        event.origin.match(/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/) &&
        new URL(event.origin).port === currentPort;
      if (event.origin !== window.location.origin && !isLocalhostSamePort) return;
      if (event.data?.type === "oauth_callback") {
        handleCallback(event.data.data);
      }
    };
    window.addEventListener("message", handleMessage);

    // Method 2: BroadcastChannel
    let channel;
    try {
      channel = new BroadcastChannel("oauth_callback");
      channel.onmessage = (event) => handleCallback(event.data);
    } catch (e) {
      console.log("BroadcastChannel not supported");
    }

    // Method 3: localStorage event
    const handleStorage = (event) => {
      if (event.key === "oauth_callback" && event.newValue) {
        try {
          const data = JSON.parse(event.newValue);
          handleCallback(data);
          localStorage.removeItem("oauth_callback");
        } catch (e) {
          console.log("Failed to parse localStorage data");
        }
      }
    };
    window.addEventListener("storage", handleStorage);

    // Also check localStorage on mount (in case callback already happened)
    try {
      const stored = localStorage.getItem("oauth_callback");
      if (stored) {
        const data = JSON.parse(stored);
        // Only use if recent (within 30 seconds)
        if (data.timestamp && Date.now() - data.timestamp < 30000) {
          handleCallback(data);
          localStorage.removeItem("oauth_callback");
        }
      }
    } catch {
      // localStorage may be unavailable or data may be malformed - ignore silently
    }

    return () => {
      window.removeEventListener("message", handleMessage);
      window.removeEventListener("storage", handleStorage);
      if (channel) channel.close();
    };
  }, [authData, exchangeTokens]);

  // Handle manual URL input
  const handleManualSubmit = async () => {
    try {
      setError(null);

      let code, state;
      const input = callbackUrl.trim();

      // Check if input looks like a URL or just a code
      if (input.includes("://") || input.startsWith("http")) {
        // Parse as URL
        const url = new URL(input);
        code = url.searchParams.get("code");
        state = url.searchParams.get("state");
        const errorParam = url.searchParams.get("error");

        if (errorParam) {
          throw new Error(url.searchParams.get("error_description") || errorParam);
        }

        if (!code) {
          throw new Error("No authorization code found in URL");
        }
      } else {
        // Treat as raw code (for iFlow and similar providers)
        code = input;
        state = authData?.state || null;
      }

      await exchangeTokens(code, state);
    } catch (err) {
      setError(err.message);
      setStep("error");
    }
  };

  if (!provider || !providerInfo) return null;

  return (
    <Modal isOpen={isOpen} title={`Connect ${providerInfo.name}`} onClose={onClose} size="lg">
      <div className="flex flex-col gap-4">
        {/* Method Selection for iFlow */}
        {step === "method-select" && provider === "iflow" && (
          <>
            <div className="space-y-4">
              <p className="text-sm text-text-muted mb-4">
                Choose how you want to connect to iFlow:
              </p>

              <button
                onClick={() => {
                  setAuthMethod("oauth");
                  startOAuthFlow();
                }}
                className="w-full p-4 rounded-lg border border-border hover:border-primary/50 hover:bg-primary/5 transition-all text-left"
              >
                <div className="flex items-start gap-3">
                  <span className="material-symbols-outlined text-2xl text-primary">key</span>
                  <div>
                    <h4 className="font-semibold mb-1">OAuth Authentication</h4>
                    <p className="text-xs text-text-muted">
                      Standard OAuth flow with browser authorization. Requires callback server.
                    </p>
                  </div>
                </div>
              </button>

              <button
                onClick={() => {
                  setAuthMethod("cookie");
                  setStep("cookie-input");
                }}
                className="w-full p-4 rounded-lg border border-border hover:border-primary/50 hover:bg-primary/5 transition-all text-left"
              >
                <div className="flex items-start gap-3">
                  <span className="material-symbols-outlined text-2xl text-green-500">cookie</span>
                  <div>
                    <h4 className="font-semibold mb-1">Cookie Authentication</h4>
                    <p className="text-xs text-text-muted">
                      Simpler method using browser cookies. Works on HF Spaces and restricted
                      platforms.
                    </p>
                    <p className="text-xs text-green-500 mt-1">✓ Recommended for HF Spaces</p>
                  </div>
                </div>
              </button>
            </div>

            <Button onClick={onClose} variant="ghost" fullWidth>
              Cancel
            </Button>
          </>
        )}

        {/* Cookie Input for iFlow */}
        {step === "cookie-input" && provider === "iflow" && (
          <>
            <div className="space-y-4">
              <div className="rounded-lg border border-blue-500/30 bg-blue-500/10 p-3 text-xs text-blue-200">
                <span className="material-symbols-outlined text-sm align-middle mr-1">info</span>
                <strong>How to get your cookie:</strong>
                <ol className="mt-2 ml-4 space-y-1 list-decimal">
                  <li>Go to https://platform.iflow.cn/ and log in</li>
                  <li>Press F12 to open DevTools → Application tab</li>
                  <li>Find Cookies → https://platform.iflow.cn</li>
                  <li>
                    Copy the <code className="bg-black/30 px-1 rounded">BXAuth</code> value
                  </li>
                </ol>
              </div>

              <div>
                <label className="text-sm font-medium mb-2 block">
                  Paste your cookie (BXAuth value)
                </label>
                <textarea
                  value={cookieValue}
                  onChange={(e) => setCookieValue(e.target.value)}
                  placeholder="BXAuth=your-cookie-value-here;"
                  className="w-full px-3 py-2 rounded-lg border border-border bg-surface text-text-main font-mono text-xs resize-none"
                  rows={4}
                />
                <p className="text-xs text-text-muted mt-1">
                  You can paste the entire cookie string or just the BXAuth value
                </p>
              </div>

              {error && (
                <div className="rounded-lg border border-red-500/30 bg-red-500/10 p-3 text-xs text-red-200">
                  <span className="material-symbols-outlined text-sm align-middle mr-1">error</span>
                  {error}
                </div>
              )}
            </div>

            <div className="flex gap-2">
              <Button
                onClick={authenticateWithCookie}
                fullWidth
                disabled={!cookieValue.trim() || cookieAuthenticating}
                loading={cookieAuthenticating}
              >
                Connect with Cookie
              </Button>
              <Button
                onClick={() => {
                  setStep("method-select");
                  setError(null);
                }}
                variant="ghost"
                fullWidth
              >
                Back
              </Button>
            </div>
          </>
        )}

        {/* Waiting Step (Localhost - popup mode) */}
        {step === "waiting" && !isDeviceCode && (
          <div className="text-center py-6">
            <div className="size-16 mx-auto mb-4 rounded-full bg-primary/10 flex items-center justify-center">
              <span className="material-symbols-outlined text-3xl text-primary animate-spin">
                progress_activity
              </span>
            </div>
            <h3 className="text-lg font-semibold mb-2">Waiting for Authorization</h3>
            <p className="text-sm text-text-muted mb-4">
              Complete the authorization in the popup window.
            </p>
            <Button variant="ghost" onClick={() => setStep("input")}>
              Popup blocked? Enter URL manually
            </Button>
          </div>
        )}

        {/* Device Code Flow - Waiting */}
        {step === "waiting" && isDeviceCode && deviceData && (
          <>
            <div className="text-center py-4">
              <p className="text-sm text-text-muted mb-4">
                Visit the URL below and enter the code:
              </p>
              <div className="bg-sidebar p-4 rounded-lg mb-4">
                <p className="text-xs text-text-muted mb-1">Verification URL</p>
                <div className="flex items-center gap-2">
                  <code className="flex-1 text-sm break-all">{deviceData.verification_uri}</code>
                  <Button
                    size="sm"
                    variant="ghost"
                    icon={copied === "verify_url" ? "check" : "content_copy"}
                    onClick={() => copy(deviceData.verification_uri, "verify_url")}
                  />
                </div>
              </div>
              <div className="bg-primary/10 p-4 rounded-lg">
                <p className="text-xs text-text-muted mb-1">Your Code</p>
                <div className="flex items-center justify-center gap-2">
                  <p className="text-2xl font-mono font-bold text-primary">
                    {deviceData.user_code}
                  </p>
                  <Button
                    size="sm"
                    variant="ghost"
                    icon={copied === "user_code" ? "check" : "content_copy"}
                    onClick={() => copy(deviceData.user_code, "user_code")}
                  />
                </div>
              </div>
            </div>
            {polling && (
              <div className="flex items-center justify-center gap-2 text-sm text-text-muted">
                <span className="material-symbols-outlined animate-spin">progress_activity</span>
                Waiting for authorization...
              </div>
            )}
          </>
        )}

        {/* Manual Input Step */}
        {step === "input" && !isDeviceCode && (
          <>
            <div className="space-y-4">
              {!isLocalhost && (
                <div className="rounded-lg border border-blue-500/30 bg-blue-500/10 p-3 text-xs text-blue-200">
                  <span className="material-symbols-outlined text-sm align-middle mr-1">info</span>
                  <strong>Remote access:</strong> Since you&apos;re accessing OmniRoute remotely,
                  after authorizing you&apos;ll see an error page (localhost not found). That&apos;s
                  expected — just copy the full URL from your browser&apos;s address bar and paste
                  it below.
                </div>
              )}
              <div>
                <p className="text-sm font-medium mb-2">Step 1: Open this URL in your browser</p>
                <div className="flex gap-2">
                  <Input
                    value={authData?.authUrl || ""}
                    readOnly
                    className="flex-1 font-mono text-xs"
                  />
                  <Button
                    variant="secondary"
                    icon={copied === "auth_url" ? "check" : "content_copy"}
                    onClick={() => copy(authData?.authUrl, "auth_url")}
                  >
                    Copy
                  </Button>
                </div>
              </div>

              <div>
                <p className="text-sm font-medium mb-2">
                  Step 2: Paste the {provider === "iflow" ? "authorization code" : "callback URL"}{" "}
                  here
                </p>
                {provider === "iflow" ? (
                  <p className="text-xs text-text-muted mb-2">
                    After authorization, iFlow will display a code on their page. Copy and paste it
                    here. You can paste either the full URL or just the code itself.
                  </p>
                ) : (
                  <p className="text-xs text-text-muted mb-2">
                    After authorization, copy the full URL from your browser.
                  </p>
                )}
                <Input
                  value={callbackUrl}
                  onChange={(e) => setCallbackUrl(e.target.value)}
                  placeholder={
                    provider === "iflow"
                      ? "3db665bf58654ad782a7163343f3f512 or full URL"
                      : placeholderUrl
                  }
                  className="font-mono text-xs"
                />
              </div>
            </div>

            <div className="flex gap-2">
              <Button onClick={handleManualSubmit} fullWidth disabled={!callbackUrl}>
                Connect
              </Button>
              <Button onClick={onClose} variant="ghost" fullWidth>
                Cancel
              </Button>
            </div>
          </>
        )}

        {/* Success Step */}
        {step === "success" && (
          <div className="text-center py-6">
            <div className="size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
              <span className="material-symbols-outlined text-3xl text-green-600">
                check_circle
              </span>
            </div>
            <h3 className="text-lg font-semibold mb-2">Connected Successfully!</h3>
            <p className="text-sm text-text-muted mb-4">
              Your {providerInfo.name} account has been connected.
            </p>
            <Button onClick={onClose} fullWidth>
              Done
            </Button>
          </div>
        )}

        {/* Error Step */}
        {step === "error" && (
          <div className="text-center py-6">
            <div className="size-16 mx-auto mb-4 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
              <span className="material-symbols-outlined text-3xl text-red-600">error</span>
            </div>
            <h3 className="text-lg font-semibold mb-2">Connection Failed</h3>
            <p className="text-sm text-red-600 mb-4">{error}</p>

            {/* Suggest cookie auth for iFlow OAuth failures */}
            {provider === "iflow" && authMethod === "oauth" && (
              <div className="rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3 text-xs text-yellow-200 mb-4 text-left">
                <span className="material-symbols-outlined text-sm align-middle mr-1">
                  lightbulb
                </span>
                <strong>Alternative:</strong> Try cookie authentication instead. It&apos;s simpler
                and works on restricted platforms.
              </div>
            )}

            <div className="flex gap-2">
              {provider === "iflow" && authMethod === "oauth" && (
                <Button
                  onClick={() => {
                    setAuthMethod("cookie");
                    setStep("cookie-input");
                    setError(null);
                  }}
                  variant="secondary"
                  fullWidth
                >
                  Try Cookie Auth
                </Button>
              )}
              <Button onClick={startOAuthFlow} variant="secondary" fullWidth>
                Try Again
              </Button>
              <Button onClick={onClose} variant="ghost" fullWidth>
                Cancel
              </Button>
            </div>
          </div>
        )}
      </div>
    </Modal>
  );
}

OAuthModal.propTypes = {
  isOpen: PropTypes.bool.isRequired,
  provider: PropTypes.string,
  providerInfo: PropTypes.shape({
    name: PropTypes.string,
  }),
  onSuccess: PropTypes.func,
  onClose: PropTypes.func.isRequired,
};
