import React, { useCallback, useEffect, useRef, useState } from 'react'
import {
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  logEvent,
} from 'src/services/analytics/index.js'
import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js'
import { Spinner } from '../../components/Spinner.js'
import TextInput from '../../components/TextInput.js'
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'
import { setClipboard } from '../../ink/termio/osc.js'
import { Box, Link, Text } from '../../ink.js'
import { OAuthService } from '../../services/oauth/index.js'
import { saveOAuthTokensIfNeeded } from '../../utils/auth.js'
import { logError } from '../../utils/log.js'

interface OAuthFlowStepProps {
  onSuccess: (token: string) => void
  onCancel: () => void
}

type OAuthStatus =
  | { state: 'starting' }
  | { state: 'waiting_for_login'; url: string }
  | { state: 'processing' }
  | { state: 'success'; token: string }
  | { state: 'error'; message: string; toRetry?: OAuthStatus }
  | { state: 'about_to_retry'; nextState: OAuthStatus }

const PASTE_HERE_MSG = 'Paste code here if prompted > '

export function OAuthFlowStep({
  onSuccess,
  onCancel,
}: OAuthFlowStepProps): React.ReactNode {
  const [oauthStatus, setOAuthStatus] = useState<OAuthStatus>({
    state: 'starting',
  })
  const [oauthService] = useState(() => new OAuthService())
  const [pastedCode, setPastedCode] = useState('')
  const [cursorOffset, setCursorOffset] = useState(0)
  const [showPastePrompt, setShowPastePrompt] = useState(false)
  const [urlCopied, setUrlCopied] = useState(false)
  const timersRef = useRef<Set<NodeJS.Timeout>>(new Set())
  // Separate ref so startOAuth's timer clear doesn't cancel the urlCopied reset
  const urlCopiedTimerRef = useRef<NodeJS.Timeout | undefined>(undefined)

  const terminalSize = useTerminalSize()
  const textInputColumns = Math.max(
    50,
    terminalSize.columns - PASTE_HERE_MSG.length - 4,
  )

  function handleKeyDown(e: KeyboardEvent): void {
    if (oauthStatus.state !== 'error') return
    e.preventDefault()
    if (e.key === 'return' && oauthStatus.toRetry) {
      setPastedCode('')
      setCursorOffset(0)
      setOAuthStatus({
        state: 'about_to_retry',
        nextState: oauthStatus.toRetry,
      })
    } else {
      onCancel()
    }
  }

  async function handleSubmitCode(value: string, url: string) {
    try {
      // Expecting format "authorizationCode#state" from the authorization callback URL
      const [authorizationCode, state] = value.split('#')

      if (!authorizationCode || !state) {
        setOAuthStatus({
          state: 'error',
          message: 'Invalid code. Please make sure the full code was copied',
          toRetry: { state: 'waiting_for_login', url },
        })
        return
      }

      // Track which path the user is taking (manual code entry)
      logEvent('tengu_oauth_manual_entry', {})
      oauthService.handleManualAuthCodeInput({
        authorizationCode,
        state,
      })
    } catch (err: unknown) {
      logError(err)
      setOAuthStatus({
        state: 'error',
        message: (err as Error).message,
        toRetry: { state: 'waiting_for_login', url },
      })
    }
  }

  const startOAuth = useCallback(async () => {
    // Clear any existing timers when starting new OAuth flow
    timersRef.current.forEach(timer => clearTimeout(timer))
    timersRef.current.clear()

    try {
      const result = await oauthService.startOAuthFlow(
        async url => {
          setOAuthStatus({ state: 'waiting_for_login', url })
          const timer = setTimeout(setShowPastePrompt, 3000, true)
          timersRef.current.add(timer)
        },
        {
          loginWithClaudeAi: true, // Always use Claude AI for subscription tokens
          inferenceOnly: true,
          expiresIn: 365 * 24 * 60 * 60, // 1 year
        },
      )

      // Show processing state
      setOAuthStatus({ state: 'processing' })

      // OAuthFlowStep creates inference-only tokens for GitHub Actions, not a
      // replacement login. Use saveOAuthTokensIfNeeded directly to avoid
      // performLogout which would destroy the user's existing auth session.
      saveOAuthTokensIfNeeded(result)

      // For OAuth flow, the access token can be used as an API key
      const timer1 = setTimeout(
        (setOAuthStatus, accessToken, onSuccess, timersRef) => {
          setOAuthStatus({ state: 'success', token: accessToken })
          // Auto-continue after brief delay to show success
          const timer2 = setTimeout(onSuccess, 1000, accessToken)
          timersRef.current.add(timer2)
        },
        100,
        setOAuthStatus,
        result.accessToken,
        onSuccess,
        timersRef,
      )
      timersRef.current.add(timer1)
    } catch (err) {
      const errorMessage = (err as Error).message
      setOAuthStatus({
        state: 'error',
        message: errorMessage,
        toRetry: { state: 'starting' }, // Allow retry by starting fresh OAuth flow
      })
      logError(err)
      logEvent('tengu_oauth_error', {
        error:
          errorMessage as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
      })
    }
  }, [oauthService, onSuccess])

  useEffect(() => {
    if (oauthStatus.state === 'starting') {
      void startOAuth()
    }
  }, [oauthStatus.state, startOAuth])

  // Retry logic
  useEffect(() => {
    if (oauthStatus.state === 'about_to_retry') {
      const timer = setTimeout(
        (nextState, setShowPastePrompt, setOAuthStatus) => {
          // Only show paste prompt when retrying to waiting_for_login
          setShowPastePrompt(nextState.state === 'waiting_for_login')
          setOAuthStatus(nextState)
        },
        500,
        oauthStatus.nextState,
        setShowPastePrompt,
        setOAuthStatus,
      )
      timersRef.current.add(timer)
    }
  }, [oauthStatus])

  useEffect(() => {
    if (
      pastedCode === 'c' &&
      oauthStatus.state === 'waiting_for_login' &&
      showPastePrompt &&
      !urlCopied
    ) {
      void setClipboard(oauthStatus.url).then(raw => {
        if (raw) process.stdout.write(raw)
        setUrlCopied(true)
        clearTimeout(urlCopiedTimerRef.current)
        urlCopiedTimerRef.current = setTimeout(setUrlCopied, 2000, false)
      })
      setPastedCode('')
    }
  }, [pastedCode, oauthStatus, showPastePrompt, urlCopied])

  // Cleanup OAuth service and timers when component unmounts
  useEffect(() => {
    const timers = timersRef.current
    return () => {
      oauthService.cleanup()
      // Clear all timers
      timers.forEach(timer => clearTimeout(timer))
      timers.clear()
      clearTimeout(urlCopiedTimerRef.current)
    }
  }, [oauthService])

  // Helper function to render the appropriate status message
  function renderStatusMessage(): React.ReactNode {
    switch (oauthStatus.state) {
      case 'starting':
        return (
          <Box>
            <Spinner />
            <Text>Starting authentication…</Text>
          </Box>
        )

      case 'waiting_for_login':
        return (
          <Box flexDirection="column" gap={1}>
            {!showPastePrompt && (
              <Box>
                <Spinner />
                <Text>
                  Opening browser to sign in with your Claude account…
                </Text>
              </Box>
            )}

            {showPastePrompt && (
              <Box>
                <Text>{PASTE_HERE_MSG}</Text>
                <TextInput
                  value={pastedCode}
                  onChange={setPastedCode}
                  onSubmit={(value: string) =>
                    handleSubmitCode(value, oauthStatus.url)
                  }
                  cursorOffset={cursorOffset}
                  onChangeCursorOffset={setCursorOffset}
                  columns={textInputColumns}
                />
              </Box>
            )}
          </Box>
        )

      case 'processing':
        return (
          <Box>
            <Spinner />
            <Text>Processing authentication…</Text>
          </Box>
        )

      case 'success':
        return (
          <Box flexDirection="column" gap={1}>
            <Text color="success">
              ✓ Authentication token created successfully!
            </Text>
            <Text dimColor>Using token for GitHub Actions setup…</Text>
          </Box>
        )

      case 'error':
        return (
          <Box flexDirection="column" gap={1}>
            <Text color="error">OAuth error: {oauthStatus.message}</Text>
            {oauthStatus.toRetry ? (
              <Text dimColor>
                Press Enter to try again, or any other key to cancel
              </Text>
            ) : (
              <Text dimColor>Press any key to return to API key selection</Text>
            )}
          </Box>
        )

      case 'about_to_retry':
        return (
          <Box flexDirection="column" gap={1}>
            <Text color="permission">Retrying…</Text>
          </Box>
        )

      default:
        return null
    }
  }

  return (
    <Box
      flexDirection="column"
      gap={1}
      tabIndex={0}
      autoFocus
      onKeyDown={handleKeyDown}
    >
      {/* Show header inline only for initial starting state */}
      {oauthStatus.state === 'starting' && (
        <Box flexDirection="column" gap={1} paddingBottom={1}>
          <Text bold>Create Authentication Token</Text>
          <Text dimColor>Creating a long-lived token for GitHub Actions</Text>
        </Box>
      )}
      {/* Show header for non-starting states (to avoid duplicate with inline header)*/}
      {oauthStatus.state !== 'success' &&
        oauthStatus.state !== 'starting' &&
        oauthStatus.state !== 'processing' && (
          <Box key="header" flexDirection="column" gap={1} paddingBottom={1}>
            <Text bold>Create Authentication Token</Text>
            <Text dimColor>Creating a long-lived token for GitHub Actions</Text>
          </Box>
        )}
      {/* Show URL when paste prompt is visible */}
      {oauthStatus.state === 'waiting_for_login' && showPastePrompt && (
        <Box flexDirection="column" key="urlToCopy" gap={1} paddingBottom={1}>
          <Box paddingX={1}>
            <Text dimColor>
              Browser didn&apos;t open? Use the url below to sign in{' '}
            </Text>
            {urlCopied ? (
              <Text color="success">(Copied!)</Text>
            ) : (
              <Text dimColor>
                <KeyboardShortcutHint shortcut="c" action="copy" parens />
              </Text>
            )}
          </Box>
          <Link url={oauthStatus.url}>
            <Text dimColor>{oauthStatus.url}</Text>
          </Link>
        </Box>
      )}
      <Box paddingLeft={1} flexDirection="column" gap={1}>
        {renderStatusMessage()}
      </Box>
    </Box>
  )
}
