/**
 * LoginPage — real login form (M2-T07, extended M4-T08).
 *
 * Behaviours:
 * - Renders a Mantine form with username + password fields (step 1).
 * - On submit → POST /api/auth/login via apiClient (no CSRF needed; unauthenticated endpoint).
 * - On success → invalidate ['session'] so SessionProvider re-fetches, then navigate to the
 *   originally-requested route (from location.state.from) or fall back to '/'.
 * - On 401 with totp_required=true → switch to step 2 (TOTP code entry).
 * - On step 2 submit → POST /api/auth/login with {username, password, totp_code}.
 * - On 401 plain (bad credentials / wrong TOTP code) → show inline error.
 * - On 429 (rate-limited) → show "too many attempts, please try again later".
 * - Already-authenticated users visiting /login → redirect to '/'.
 *
 * Security:
 * - Password is retained in state for the step-2 re-submission but is NEVER
 *   written to localStorage, sessionStorage, or console.
 * - totp_code / recovery codes are NEVER logged or persisted.
 */

import { useState } from 'react'
import { useNavigate, useLocation, Navigate } from 'react-router-dom'
import { useQueryClient } from '@tanstack/react-query'
import {
  Container,
  Paper,
  Title,
  TextInput,
  PasswordInput,
  Button,
  Alert,
  Stack,
  Center,
  Text,
} from '@mantine/core'
import { useSession } from '../auth/SessionProvider'
import apiClient, { ApiError } from '../api/client'
import { setCsrfToken } from '../api/csrf'

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface LocationState {
  from?: { pathname: string }
}

/** The two UI states for the login flow. */
type LoginStep = 'credentials' | 'totp'

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export function LoginPage() {
  const { status } = useSession()
  const navigate = useNavigate()
  const location = useLocation()
  const queryClient = useQueryClient()

  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [totpCode, setTotpCode] = useState('')
  const [step, setStep] = useState<LoginStep>('credentials')
  const [error, setError] = useState<string | null>(null)
  const [loading, setLoading] = useState(false)

  // Already authenticated → redirect to intended destination or home.
  if (status === 'authenticated') {
    const from = (location.state as LocationState)?.from?.pathname ?? '/'
    return <Navigate to={from} replace />
  }

  // ---------------------------------------------------------------------------
  // handleCredentialsSubmit — step 1
  // ---------------------------------------------------------------------------

  async function handleCredentialsSubmit(e: React.FormEvent) {
    e.preventDefault()
    setError(null)
    setLoading(true)

    try {
      const res = await apiClient.POST('/api/auth/login', {
        body: { username, password },
      })

      if (res.response.status === 401 || !res.data) {
        // Check if the backend wants a TOTP code.
        // openapi-fetch puts 401 body in res.error (not res.data).
        // The shape is { detail: { totp_required: true } } for TOTP-required,
        // or { detail: "invalid username or password" } for bad credentials.
        const detail = (res.error as { detail?: unknown } | undefined)?.detail
        if (detail && typeof detail === 'object' && (detail as { totp_required?: boolean }).totp_required === true) {
          // Transition to step 2 — TOTP entry. Password stays in state for re-send.
          setStep('totp')
          setTotpCode('')
          return
        }
        // Ordinary bad credentials.
        setError('Incorrect username or password.')
        return
      }

      // Success: store the CSRF token returned by login.
      if (res.data.csrf_token) {
        setCsrfToken(res.data.csrf_token)
      }

      // Refresh session state.
      await queryClient.invalidateQueries({ queryKey: ['session'] })
      const from = (location.state as LocationState)?.from?.pathname ?? '/'
      navigate(from, { replace: true })
    } catch (err) {
      if (err instanceof ApiError && err.status === 429) {
        setError('Too many login attempts. Please try again later.')
      } else {
        setError('Login failed. Please try again.')
      }
    } finally {
      setLoading(false)
    }
  }

  // ---------------------------------------------------------------------------
  // handleTotpSubmit — step 2
  // ---------------------------------------------------------------------------

  async function handleTotpSubmit(e: React.FormEvent) {
    e.preventDefault()
    setError(null)
    setLoading(true)

    try {
      const res = await apiClient.POST('/api/auth/login', {
        body: { username, password, totp_code: totpCode },
      })

      if (res.response.status === 401 || !res.data) {
        // Wrong code (or expired) — stay on step 2 and show error.
        setError('Incorrect verification code. Please try again.')
        return
      }

      // Success.
      if (res.data.csrf_token) {
        setCsrfToken(res.data.csrf_token)
      }
      await queryClient.invalidateQueries({ queryKey: ['session'] })
      const from = (location.state as LocationState)?.from?.pathname ?? '/'
      navigate(from, { replace: true })
    } catch (err) {
      if (err instanceof ApiError && err.status === 429) {
        setError('Too many login attempts. Please try again later.')
      } else {
        setError('Login failed. Please try again.')
      }
    } finally {
      setLoading(false)
    }
  }

  // ---------------------------------------------------------------------------
  // Render
  // ---------------------------------------------------------------------------

  return (
    <Center mih="100vh">
      <Container size="xs" w="100%">
        <Paper shadow="sm" p="xl" radius="md" withBorder>
          <Title order={2} mb="lg" ta="center">
            Sign In
          </Title>

          {error && (
            <Alert color="red" mb="md" role="alert" data-testid="login-error">
              {error}
            </Alert>
          )}

          {step === 'credentials' && (
            <form onSubmit={handleCredentialsSubmit} data-testid="login-form">
              <Stack gap="md">
                <TextInput
                  label="Username"
                  placeholder="Enter your username"
                  value={username}
                  onChange={(e) => setUsername(e.currentTarget.value)}
                  required
                  autoComplete="username"
                  data-testid="username-input"
                />

                <PasswordInput
                  label="Password"
                  placeholder="Enter your password"
                  value={password}
                  onChange={(e) => setPassword(e.currentTarget.value)}
                  required
                  autoComplete="current-password"
                  data-testid="password-input"
                />

                <Button
                  type="submit"
                  fullWidth
                  loading={loading}
                  mt="sm"
                  data-testid="login-submit"
                >
                  Sign In
                </Button>
              </Stack>
            </form>
          )}

          {step === 'totp' && (
            <form onSubmit={handleTotpSubmit} data-testid="login-totp-form">
              <Stack gap="md">
                <Text size="sm" c="dimmed">
                  Enter the 6-digit code from your authenticator app, or a recovery code.
                </Text>

                <TextInput
                  label="Verification Code"
                  placeholder="6-digit code or recovery code"
                  value={totpCode}
                  onChange={(e) => setTotpCode(e.currentTarget.value)}
                  required
                  autoComplete="one-time-code"
                  inputMode="numeric"
                  data-testid="totp-code-input"
                />

                <Button
                  type="submit"
                  fullWidth
                  loading={loading}
                  mt="sm"
                  data-testid="totp-submit"
                >
                  Verify
                </Button>

                <Button
                  variant="subtle"
                  size="sm"
                  onClick={() => {
                    setStep('credentials')
                    setError(null)
                    setTotpCode('')
                  }}
                  data-testid="totp-back"
                >
                  Back to login
                </Button>
              </Stack>
            </form>
          )}
        </Paper>
      </Container>
    </Center>
  )
}
