import { useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { authApi } from '@/api/auth'
import { useAuthStore } from '@/store/authStore'
import { BrandLogo } from '@/components/common/BrandLogo'
import { PageMeta } from '@/components/common/PageMeta'
import { decodeOAuthState } from '@/lib/oauthState'

type Provider = 'google' | 'microsoft'

/**
 * Handles the OAuth redirect leg of the full-page Google / Microsoft sign-in
 * flow. Mounted at /auth/google/callback and /auth/microsoft/callback as
 * public routes (NOT inside ProtectedRoute).
 *
 * Reads `?code=...` from the URL, POSTs it to the backend, stores the
 * returned tokens, hydrates the auth store via fetchUser(), and redirects.
 *
 * Two state forms are supported:
 *   - Legacy: `state` is a raw random hex string. CSRF check against
 *     sessionStorage('rf-oauth-state').
 *   - /accept-invite: `state` is base64url(JSON({csrf, accountInviteCode,
 *     invitedEmail})). The CSRF value is compared against
 *     sessionStorage('rf-oauth-state'); the invite fields are forwarded to
 *     the backend so the new user joins the invited account instead of
 *     getting a personal one.
 */
export function OAuthCallbackPage() {
  const navigate = useNavigate()
  const location = useLocation()
  const { setTokens, fetchUser } = useAuthStore()
  const [error, setError] = useState<string | null>(null)

  // Derive provider purely from URL pathname — routes are static
  // (/auth/google/callback and /auth/microsoft/callback), so there is
  // no `:provider` route param to read.
  const provider: Provider = location.pathname.includes('/microsoft/')
    ? 'microsoft'
    : 'google'

  useEffect(() => {
    const search = new URLSearchParams(location.search)
    const code = search.get('code')
    const oauthError = search.get('error')
    const returnedState = search.get('state')

    // CSRF: validate state round-trip against the value RegisterPage /
    // AcceptInvitePage stashed in sessionStorage before redirecting to the
    // provider. Always clear the stored value so a stale entry can't be
    // re-used by a later attempt.
    let storedState: string | null = null
    try {
      storedState = sessionStorage.getItem('rf-oauth-state')
      sessionStorage.removeItem('rf-oauth-state')
    } catch {
      // sessionStorage may be unavailable (private mode, etc.) — treat as missing.
      storedState = null
    }

    if (oauthError) {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- callback URL validation maps directly into local error state
      setError(`OAuth error: ${oauthError}`)
      return
    }
    if (!storedState || !returnedState) {
      setError('Invalid OAuth state — possible CSRF. Please try again.')
      return
    }

    // The decoded form encodes the original CSRF value; compare that.
    const decoded = decodeOAuthState(returnedState)
    const matchesCsrf = decoded
      ? decoded.csrf === storedState
      : returnedState === storedState
    if (!matchesCsrf) {
      setError('Invalid OAuth state — possible CSRF. Please try again.')
      return
    }
    if (!code) {
      setError('Missing authorization code')
      return
    }

    let cancelled = false
    void (async () => {
      try {
        const inviteOptions = decoded
          ? {
              accountInviteCode: decoded.accountInviteCode,
              invitedEmail: decoded.invitedEmail,
            }
          : undefined
        const result =
          provider === 'microsoft'
            ? await authApi.microsoftCallback(code, inviteOptions)
            : await authApi.googleCallback(code, inviteOptions)
        if (cancelled) return

        // Persist tokens for apiClient interceptor + zustand store.
        localStorage.setItem('access_token', result.access_token)
        localStorage.setItem('refresh_token', result.refresh_token)
        setTokens({
          access_token: result.access_token,
          refresh_token: result.refresh_token,
          token_type: result.token_type || 'bearer',
          idle_expires_at: result.idle_expires_at,
          absolute_expires_at: result.absolute_expires_at,
        })
        // Hydrate user / account / subscription.
        await fetchUser()
        if (cancelled) return

        // Invitee path lands on the dashboard with the teammate-welcome
        // marker; new self-serve owners go to the welcome wizard; returning
        // users to /home.
        let dest = '/home'
        if (decoded?.accountInviteCode) {
          dest = '/home?welcome=teammate'
        } else if (result.is_new_user) {
          dest = '/welcome'
        }
        navigate(dest, { replace: true })
      } catch (err: unknown) {
        if (cancelled) return
        const axiosErr = err as {
          response?: { data?: { detail?: unknown } }
        }
        const detail = axiosErr.response?.data?.detail
        // Backend returns { error: "invite_email_mismatch" } etc.
        let msg: string | null = null
        if (typeof detail === 'string') {
          msg = detail
        } else if (
          detail &&
          typeof detail === 'object' &&
          'error' in (detail as Record<string, unknown>)
        ) {
          const code = (detail as { error: string }).error
          if (code === 'invite_email_mismatch') {
            msg =
              'The email on your provider account does not match the invited email. ' +
              'Sign in with the matching account, or ask your inviter to resend.'
          } else if (code === 'invite_invalid_or_expired_or_revoked') {
            msg = 'This invite is no longer valid. Ask your inviter to resend.'
          } else {
            msg = code
          }
        }
        msg =
          msg ||
          (err instanceof Error ? err.message : 'Sign-in failed')
        setError(msg)
      }
    })()

    return () => {
      cancelled = true
    }
  }, [location.search, provider, setTokens, fetchUser, navigate])

  return (
    <>
      <PageMeta title="Signing you in" description="Completing OAuth sign-in" />
      <div className="flex min-h-screen items-center justify-center bg-black px-4">
        <div className="relative w-full max-w-md space-y-6 text-center">
          <div className="flex justify-center">
            <BrandLogo size="lg" />
          </div>
          {error ? (
            <>
              <h1 className="text-xl font-semibold text-foreground">
                Sign-in failed
              </h1>
              <p className="text-sm text-red-400">{error}</p>
              <button
                onClick={() => navigate('/login', { replace: true })}
                className="rounded-xl bg-primary px-4 py-2 text-sm font-semibold text-white hover:brightness-110"
              >
                Back to sign in
              </button>
            </>
          ) : (
            <>
              <h1 className="text-xl font-semibold text-foreground">
                Signing you in…
              </h1>
              <p className="text-sm text-muted-foreground">
                Finishing up the {provider === 'microsoft' ? 'Microsoft' : 'Google'} sign-in flow.
              </p>
            </>
          )}
        </div>
      </div>
    </>
  )
}

export default OAuthCallbackPage
