import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { FileUploader } from '@/components/file-uploader';
import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import message from '@/components/ui/message';
import { FileMimeType } from '@/constants/common';
import {
  pollGmailWebAuthResult,
  startGmailWebAuth,
} from '@/services/data-source-service';
import { Loader2 } from 'lucide-react';

export type GmailTokenFieldProps = {
  value?: string;
  onChange: (value: any) => void;
  placeholder?: string;
};

const credentialHasRefreshToken = (content: string) => {
  try {
    const parsed = JSON.parse(content);
    return Boolean(parsed?.refresh_token);
  } catch {
    return false;
  }
};

const describeCredentials = (content?: string) => {
  if (!content) return '';
  try {
    const parsed = JSON.parse(content);
    if (parsed?.refresh_token) {
      return 'Uploaded OAuth tokens with a refresh token.';
    }
    if (parsed?.installed || parsed?.web) {
      return 'Client credentials detected. Complete verification to mint long-lived tokens.';
    }
    return 'Stored Google credential JSON.';
  } catch {
    return '';
  }
};

const parseJsonObject = (content?: string): Record<string, any> | null => {
  if (!content) return null;
  try {
    const parsed = JSON.parse(content);
    return typeof parsed === 'object' && parsed !== null ? parsed : null;
  } catch {
    return null;
  }
};

const extractRedirectUri = (content?: string): string => {
  const parsed = parseJsonObject(content);
  if (!parsed) return '';

  if (typeof parsed.redirect_uri === 'string' && parsed.redirect_uri.trim()) {
    return parsed.redirect_uri.trim();
  }

  const redirectUris =
    parsed.web?.redirect_uris ?? parsed.installed?.redirect_uris;
  if (Array.isArray(redirectUris)) {
    const firstValidRedirect = redirectUris.find(
      (item) => typeof item === 'string' && item.trim(),
    );
    if (firstValidRedirect) {
      return firstValidRedirect.trim();
    }
  }

  return '';
};

const withRedirectUri = (credentials: string, redirectUri: string): string => {
  const trimmedRedirectUri = redirectUri.trim();
  if (!trimmedRedirectUri) return credentials;

  const parsed = parseJsonObject(credentials);
  if (!parsed) return credentials;

  return JSON.stringify({
    ...parsed,
    redirect_uri: trimmedRedirectUri,
  });
};

const GmailTokenField = ({ value, onChange }: GmailTokenFieldProps) => {
  const [files, setFiles] = useState<File[]>([]);
  const [pendingCredentials, setPendingCredentials] = useState<string>('');
  const [redirectUri, setRedirectUri] = useState('');
  const [dialogOpen, setDialogOpen] = useState(false);
  const [webAuthLoading, setWebAuthLoading] = useState(false);
  const [webFlowId, setWebFlowId] = useState<string | null>(null);
  const [webStatus, setWebStatus] = useState<
    'idle' | 'waiting' | 'success' | 'error'
  >('idle');
  const [webStatusMessage, setWebStatusMessage] = useState('');
  const webFlowIdRef = useRef<string | null>(null);
  const webPollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const clearWebState = useCallback(() => {
    if (webPollTimerRef.current) {
      clearTimeout(webPollTimerRef.current);
      webPollTimerRef.current = null;
    }
    webFlowIdRef.current = null;
    setWebFlowId(null);
    setWebStatus('idle');
    setWebStatusMessage('');
  }, []);

  useEffect(() => {
    return () => {
      if (webPollTimerRef.current) {
        clearTimeout(webPollTimerRef.current);
      }
    };
  }, []);

  useEffect(() => {
    webFlowIdRef.current = webFlowId;
  }, [webFlowId]);

  useEffect(() => {
    if (!dialogOpen) {
      setRedirectUri(extractRedirectUri(value));
    }
  }, [dialogOpen, value]);

  const credentialSummary = useMemo(() => describeCredentials(value), [value]);
  const hasVerifiedTokens = useMemo(
    () => Boolean(value && credentialHasRefreshToken(value)),
    [value],
  );
  const hasUploadedButUnverified = useMemo(
    () => Boolean(value && !hasVerifiedTokens),
    [hasVerifiedTokens, value],
  );

  const resetDialog = useCallback(
    (shouldResetState: boolean) => {
      setDialogOpen(false);
      clearWebState();
      if (shouldResetState) {
        setPendingCredentials('');
        setFiles([]);
      }
    },
    [clearWebState],
  );

  const fetchWebResult = useCallback(
    async (flowId: string) => {
      try {
        const { data } = await pollGmailWebAuthResult({
          flow_id: flowId,
        });
        if (data.code === 0 && data.data?.credentials) {
          const rawCredentials =
            typeof data.data.credentials === 'string'
              ? data.data.credentials
              : JSON.stringify(data.data.credentials);
          onChange(withRedirectUri(rawCredentials, redirectUri));
          setPendingCredentials('');
          message.success('Gmail credentials verified.');
          resetDialog(false);
          return;
        }
        if (data.code === 106) {
          setWebStatus('waiting');
          setWebStatusMessage('Authorization confirmed. Finalizing tokens...');
          if (webPollTimerRef.current) {
            clearTimeout(webPollTimerRef.current);
          }
          webPollTimerRef.current = setTimeout(
            () => fetchWebResult(flowId),
            1500,
          );
          return;
        }
        message.error(data.message || 'Authorization failed.');
        clearWebState();
      } catch {
        message.error('Unable to retrieve authorization result.');
        clearWebState();
      }
    },
    [clearWebState, onChange, redirectUri, resetDialog],
  );

  useEffect(() => {
    const handler = (event: MessageEvent) => {
      const payload = event.data;
      if (!payload || payload.type !== 'ragflow-gmail-oauth') {
        return;
      }
      if (!payload.flowId) {
        return;
      }
      if (webFlowIdRef.current && webFlowIdRef.current !== payload.flowId) {
        return;
      }

      if (payload.status === 'success') {
        setWebStatus('waiting');
        setWebStatusMessage('Authorization confirmed. Finalizing tokens...');
        fetchWebResult(payload.flowId);
      } else {
        message.error(
          payload.message || 'Authorization window reported an error.',
        );
        clearWebState();
      }
    };

    window.addEventListener('message', handler);
    return () => window.removeEventListener('message', handler);
  }, [clearWebState, fetchWebResult]);

  const handleValueChange = useCallback(
    (nextFiles: File[]) => {
      if (!nextFiles.length) {
        setFiles([]);
        onChange('');
        setPendingCredentials('');
        clearWebState();
        return;
      }
      const file = nextFiles[nextFiles.length - 1];
      file
        .text()
        .then((text) => {
          try {
            JSON.parse(text);
          } catch {
            message.error('Invalid JSON file.');
            setFiles([]);
            clearWebState();
            return;
          }
          setFiles([file]);
          clearWebState();
          const extractedRedirectUri = extractRedirectUri(text);
          setRedirectUri(extractedRedirectUri);
          if (credentialHasRefreshToken(text)) {
            onChange(withRedirectUri(text, extractedRedirectUri));
            setPendingCredentials('');
            message.success('Gmail OAuth credentials uploaded.');
            return;
          }
          setPendingCredentials(text);
          setDialogOpen(true);
          message.info(
            'Client configuration uploaded. Verification is required to finish setup.',
          );
        })
        .catch(() => {
          message.error('Unable to read the uploaded file.');
          setFiles([]);
        });
    },
    [clearWebState, onChange],
  );

  const handleStartWebAuthorization = useCallback(async () => {
    if (!pendingCredentials) {
      message.error('No Google credential file detected.');
      return;
    }
    if (!redirectUri.trim()) {
      message.error('Please fill in Redirect URI.');
      return;
    }
    const trimmedRedirectUri = redirectUri.trim();
    setWebAuthLoading(true);
    clearWebState();
    try {
      const { data } = await startGmailWebAuth({
        credentials: pendingCredentials,
        redirect_uri: trimmedRedirectUri,
      });
      if (data.code === 0 && data.data?.authorization_url) {
        const flowId = data.data.flow_id;
        const popup = window.open(
          data.data.authorization_url,
          'ragflow-gmail-oauth',
          'width=600,height=720',
        );
        if (!popup) {
          message.error(
            'Popup was blocked. Please allow popups for this site.',
          );
          return;
        }
        popup.focus();
        webFlowIdRef.current = flowId;
        setWebFlowId(flowId);
        setWebStatus('waiting');
        setWebStatusMessage('Complete the Google consent in the popup window.');
      } else {
        message.error(data.message || 'Failed to start browser authorization.');
      }
    } catch {
      message.error('Failed to start browser authorization.');
    } finally {
      setWebAuthLoading(false);
    }
  }, [clearWebState, pendingCredentials, redirectUri]);

  const handleManualWebCheck = useCallback(() => {
    if (!webFlowId) {
      message.info('Start browser authorization first.');
      return;
    }
    setWebStatus('waiting');
    setWebStatusMessage('Checking authorization status...');
    fetchWebResult(webFlowId);
  }, [fetchWebResult, webFlowId]);

  const handleCancel = useCallback(() => {
    message.warning(
      'Verification canceled. Upload the credential again to restart.',
    );
    resetDialog(true);
  }, [resetDialog]);

  return (
    <div className="flex w-full flex-col gap-3">
      {(credentialSummary ||
        hasVerifiedTokens ||
        hasUploadedButUnverified ||
        pendingCredentials) && (
        <div className="flex flex-wrap items-center gap-3 rounded-md border border-dashed border-muted-foreground/40 bg-muted/20 px-3 py-2 text-xs text-muted-foreground">
          <div className="flex flex-wrap items-center gap-2">
            {hasVerifiedTokens ? (
              <span className="rounded-full bg-emerald-100 px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-emerald-700">
                Verified
              </span>
            ) : null}
            {hasUploadedButUnverified ? (
              <span className="rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-amber-700">
                Needs authorization
              </span>
            ) : null}
            {pendingCredentials && !hasVerifiedTokens ? (
              <span className="rounded-full bg-blue-100 px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-blue-700">
                Uploaded (pending)
              </span>
            ) : null}
          </div>
          {credentialSummary ? (
            <p className="m-0">{credentialSummary}</p>
          ) : null}
        </div>
      )}
      <FileUploader
        className="py-4 border-[0.5px] bg-bg-card text-text-secondary"
        value={files}
        onValueChange={handleValueChange}
        accept={{ '*.json': [FileMimeType.Json] }}
        maxFileCount={1}
        description={'Upload your Gmail OAuth JSON file.'}
      />

      <Dialog
        open={dialogOpen}
        onOpenChange={(open) => {
          if (!open && dialogOpen) {
            handleCancel();
          }
        }}
      >
        <DialogContent
          onPointerDownOutside={(e) => e.preventDefault()}
          onInteractOutside={(e) => e.preventDefault()}
          onEscapeKeyDown={(e) => e.preventDefault()}
        >
          <DialogHeader>
            <DialogTitle>Complete Gmail verification</DialogTitle>
            <DialogDescription>
              The uploaded client credentials do not contain a refresh token.
              Run the verification flow once to mint reusable tokens.
            </DialogDescription>
          </DialogHeader>

          <div className="space-y-4">
            <div className="space-y-1">
              <label className="text-sm font-medium">Redirect URI</label>
              <Input
                value={redirectUri}
                placeholder="https://example.com/gmail/oauth/callback"
                onChange={(e) => setRedirectUri(e.target.value)}
              />
            </div>
            <div className="rounded-md border border-dashed border-muted-foreground/40 bg-muted/10 px-4 py-4 text-sm text-muted-foreground">
              <div className="text-sm font-semibold text-foreground">
                Authorize in browser
              </div>
              <p className="mt-2">
                We will open Google&apos;s consent page in a new window. Sign in
                with the admin account, grant access, and return here. Your
                credentials will update automatically.
              </p>
              {webStatus !== 'idle' && (
                <p
                  className={`mt-2 text-xs ${
                    webStatus === 'error'
                      ? 'text-destructive'
                      : 'text-muted-foreground'
                  }`}
                >
                  {webStatusMessage}
                </p>
              )}
              <div className="mt-3 flex flex-wrap gap-2">
                <Button
                  onClick={handleStartWebAuthorization}
                  disabled={webAuthLoading}
                >
                  {webAuthLoading && (
                    <Loader2 className="mr-2 size-4 animate-spin" />
                  )}
                  Authorize with Google
                </Button>
                {webFlowId ? (
                  <Button
                    variant="outline"
                    onClick={handleManualWebCheck}
                    disabled={webStatus === 'success'}
                  >
                    Refresh status
                  </Button>
                ) : null}
              </div>
            </div>
          </div>

          <DialogFooter className="pt-2">
            <Button variant="ghost" onClick={handleCancel}>
              Cancel
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
};

export default GmailTokenField;
