/**
 * TotpSettings — TOTP two-factor auth management panel (M4-T08).
 *
 * Intended to be embedded in the settings/config area of the app.
 * Handles the full TOTP lifecycle:
 *   - Status probe  : GET /api/auth/totp
 *   - Enable flow   : setup → show QR code + one-time recovery codes → confirm with code
 *   - Disable flow  : prompt for current password or TOTP code → disable
 *
 * Security notes:
 * - secret and recovery_codes from TotpSetupResponse are held ONLY in component
 *   state; they are NEVER written to localStorage, sessionStorage, or console.
 * - Recovery codes are displayed exactly once (during the setup flow); after the
 *   user confirms with a code and TOTP is enabled the data is discarded.
 * - Passwords typed in the disable form are NEVER logged or persisted.
 */

import { useState, useCallback } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import {
  Paper,
  Title,
  Text,
  Button,
  Alert,
  Stack,
  Group,
  TextInput,
  PasswordInput,
  Divider,
  Badge,
  Loader,
  Center,
  Code,
  List,
} from '@mantine/core'
import { QRCodeSVG } from 'qrcode.react'
import { getTotpStatus, setupTotp, enableTotp, disableTotp } from '../auth/totp'
import { ApiError } from '../api/client'
import type { TotpSetupResponse } from '../auth/totp'

// ---------------------------------------------------------------------------
// Sub-panel: Enable flow
// ---------------------------------------------------------------------------

interface EnablePanelProps {
  onEnabled: () => void
}

function EnablePanel({ onEnabled }: EnablePanelProps) {
  const [setupData, setSetupData] = useState<TotpSetupResponse | null>(null)
  const [confirmCode, setConfirmCode] = useState('')
  const [status, setStatus] = useState<'idle' | 'setup-loading' | 'confirm' | 'confirming' | 'error'>('idle')
  const [errorMsg, setErrorMsg] = useState<string | null>(null)

  async function handleSetup() {
    setStatus('setup-loading')
    setErrorMsg(null)
    try {
      const data = await setupTotp()
      // data contains one-time secret + recovery_codes — keep only in state.
      setSetupData(data)
      setStatus('confirm')
    } catch {
      setErrorMsg('Failed to start TOTP setup. Please try again.')
      setStatus('error')
    }
  }

  async function handleEnable(e: React.FormEvent) {
    e.preventDefault()
    setErrorMsg(null)
    setStatus('confirming')
    try {
      await enableTotp(confirmCode)
      // Discard secret + recovery codes from memory now that TOTP is enabled.
      setSetupData(null)
      setConfirmCode('')
      onEnabled()
    } catch (err) {
      let msg = 'Failed to enable TOTP. Check the code and try again.'
      if (err instanceof ApiError && err.status === 400) {
        msg = 'Incorrect code or no pending setup. Please start the setup again.'
      }
      setErrorMsg(msg)
      setStatus('confirm')
    }
  }

  // ---------------------------------------------------------------------------
  // Render: idle — show "Start Setup" button
  // ---------------------------------------------------------------------------

  if (status === 'idle' || status === 'error') {
    return (
      <Stack gap="sm">
        <Text size="sm">
          Two-factor authentication is not enabled. Enable it to require a verification code
          each time you sign in.
        </Text>
        {errorMsg && (
          <Alert color="red" data-testid="totp-enable-error">
            {errorMsg}
          </Alert>
        )}
        <Button onClick={handleSetup} data-testid="totp-setup-button">
          Enable Two-Factor Authentication
        </Button>
      </Stack>
    )
  }

  // ---------------------------------------------------------------------------
  // Render: setup-loading
  // ---------------------------------------------------------------------------

  if (status === 'setup-loading') {
    return (
      <Center>
        <Loader data-testid="totp-setup-loading" />
      </Center>
    )
  }

  // ---------------------------------------------------------------------------
  // Render: confirm — show QR code, secret, recovery codes, confirmation form
  // ---------------------------------------------------------------------------

  if ((status === 'confirm' || status === 'confirming') && setupData) {
    return (
      <Stack gap="md">
        <Alert color="yellow" data-testid="totp-setup-warning">
          Save your recovery codes now — they will only be shown once.
        </Alert>

        <Text size="sm" fw={500}>
          1. Scan this QR code with your authenticator app (e.g. Google Authenticator, Authy):
        </Text>

        <Center data-testid="totp-qr-code">
          <QRCodeSVG value={setupData.otpauth_uri} size={180} />
        </Center>

        <Text size="sm" c="dimmed" ta="center">
          Or enter the key manually:
        </Text>
        <Code block data-testid="totp-secret">
          {setupData.secret}
        </Code>

        <Divider />

        <Text size="sm" fw={500}>
          2. Save these recovery codes in a secure place. Each code can be used once
          if you lose access to your authenticator:
        </Text>
        <Paper withBorder p="sm" data-testid="totp-recovery-codes">
          <List spacing="xs" size="sm">
            {setupData.recovery_codes.map((code) => (
              <List.Item key={code}>
                <Code>{code}</Code>
              </List.Item>
            ))}
          </List>
        </Paper>

        <Divider />

        <Text size="sm" fw={500}>
          3. Enter the 6-digit code from your authenticator app to confirm setup:
        </Text>

        {errorMsg && (
          <Alert color="red" data-testid="totp-confirm-error">
            {errorMsg}
          </Alert>
        )}

        <form onSubmit={handleEnable} data-testid="totp-confirm-form">
          <Stack gap="sm">
            <TextInput
              label="Verification Code"
              placeholder="6-digit code"
              value={confirmCode}
              onChange={(e) => setConfirmCode(e.currentTarget.value)}
              required
              autoComplete="one-time-code"
              inputMode="numeric"
              data-testid="totp-confirm-input"
            />
            <Button
              type="submit"
              loading={status === 'confirming'}
              data-testid="totp-confirm-button"
            >
              Confirm and Enable
            </Button>
          </Stack>
        </form>
      </Stack>
    )
  }

  return null
}

// ---------------------------------------------------------------------------
// Sub-panel: Disable flow
// ---------------------------------------------------------------------------

interface DisablePanelProps {
  onDisabled: () => void
}

function DisablePanel({ onDisabled }: DisablePanelProps) {
  const [disablePassword, setDisablePassword] = useState('')
  const [disableCode, setDisableCode] = useState('')
  const [errorMsg, setErrorMsg] = useState<string | null>(null)
  const [isSubmitting, setIsSubmitting] = useState(false)

  async function handleDisable(e: React.FormEvent) {
    e.preventDefault()
    setErrorMsg(null)

    // At least one of password or code must be provided.
    if (!disablePassword && !disableCode) {
      setErrorMsg('Please provide your current password or a TOTP code.')
      return
    }

    setIsSubmitting(true)
    try {
      await disableTotp({
        password: disablePassword || null,
        code: disableCode || null,
      })
      setDisablePassword('')
      setDisableCode('')
      onDisabled()
    } catch (err) {
      let msg = 'Failed to disable TOTP. Please try again.'
      if (err instanceof ApiError && err.status === 400) {
        msg = 'Incorrect password or code. Please try again.'
      }
      setErrorMsg(msg)
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <Stack gap="md">
      <Alert color="blue" data-testid="totp-enabled-notice">
        Two-factor authentication is enabled. To disable it, enter your current password
        or a valid verification code.
      </Alert>

      {errorMsg && (
        <Alert color="red" data-testid="totp-disable-error">
          {errorMsg}
        </Alert>
      )}

      <form onSubmit={handleDisable} data-testid="totp-disable-form">
        <Stack gap="sm">
          <PasswordInput
            label="Current Password"
            placeholder="Enter your password (or use a TOTP code below)"
            value={disablePassword}
            onChange={(e) => setDisablePassword(e.currentTarget.value)}
            autoComplete="current-password"
            data-testid="totp-disable-password"
          />

          <Text size="xs" c="dimmed" ta="center">
            — or —
          </Text>

          <TextInput
            label="TOTP Code"
            placeholder="6-digit code from your authenticator"
            value={disableCode}
            onChange={(e) => setDisableCode(e.currentTarget.value)}
            autoComplete="one-time-code"
            inputMode="numeric"
            data-testid="totp-disable-code"
          />

          <Button
            type="submit"
            color="red"
            variant="outline"
            loading={isSubmitting}
            data-testid="totp-disable-button"
          >
            Disable Two-Factor Authentication
          </Button>
        </Stack>
      </form>
    </Stack>
  )
}

// ---------------------------------------------------------------------------
// TotpSettings — main exported component
// ---------------------------------------------------------------------------

export function TotpSettings() {
  const queryClient = useQueryClient()

  const {
    data: totpStatus,
    isLoading,
    isError,
  } = useQuery({
    queryKey: ['totp-status'],
    queryFn: getTotpStatus,
    // No automatic retries — the test QueryClient is configured with retry: false.
    // Network errors are shown immediately via the error state.
    retry: false,
  })

  // Invalidate the TOTP status query to refetch after enable/disable.
  const refetchStatus = useCallback(() => {
    void queryClient.invalidateQueries({ queryKey: ['totp-status'] })
  }, [queryClient])

  return (
    <Paper withBorder p="md" radius="md" data-testid="totp-settings">
      <Group justify="space-between" mb="md" wrap="nowrap">
        <Title order={4}>Two-Factor Authentication (TOTP)</Title>
        {totpStatus && (
          <Badge
            color={totpStatus.enabled ? 'green' : 'gray'}
            variant="outline"
            data-testid="totp-status-badge"
          >
            {totpStatus.enabled ? 'Enabled' : 'Disabled'}
          </Badge>
        )}
      </Group>

      {isLoading && (
        <Center>
          <Loader data-testid="totp-status-loading" />
        </Center>
      )}

      {isError && (
        <Alert color="red" data-testid="totp-status-error">
          Failed to load TOTP status. Please refresh the page.
        </Alert>
      )}

      {totpStatus && !totpStatus.enabled && <EnablePanel onEnabled={refetchStatus} />}

      {totpStatus && totpStatus.enabled && <DisablePanel onDisabled={refetchStatus} />}
    </Paper>
  )
}
