import { cleanup, render, screen } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import userEvent from '@testing-library/user-event'
import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ROUTER_FUTURE_FLAGS } from '../router/router-future'

const mockUsePlatformAuth = vi.fn()
const mockLogin = vi.fn()
const mockRegister = vi.fn()
const mockGetSuperTokensAuthRuntimeValidation = vi.fn()
const mockIsGitHubOAuthEnabled = vi.fn(() => false)
const mockIsGoogleOAuthEnabled = vi.fn(() => false)
const mockIsOrcidOAuthEnabled = vi.fn(() => false)
const mockGetAuthHealth = vi.fn()
const mockGetReleaseManifest = vi.fn()

vi.mock('../auth/platform-auth', () => ({
  usePlatformAuth: () => mockUsePlatformAuth(),
}))

vi.mock('../auth/auth-api', () => ({
  getAuthHealth: (...args: unknown[]) => mockGetAuthHealth(...args),
  getReleaseManifest: (...args: unknown[]) => mockGetReleaseManifest(...args),
}))

vi.mock('../auth/supertokens-runtime', () => ({
  getSuperTokensAuthRuntimeValidation: () => mockGetSuperTokensAuthRuntimeValidation(),
  isGitHubOAuthEnabled: () => mockIsGitHubOAuthEnabled(),
  isGoogleOAuthEnabled: () => mockIsGoogleOAuthEnabled(),
  isOrcidOAuthEnabled: () => mockIsOrcidOAuthEnabled(),
}))

vi.mock('../site-config', async () => {
  const actual = await vi.importActual<typeof import('../site-config')>('../site-config')
  return {
    ...actual,
    downloadTargets: [
      {
        platformKey: 'windows',
        platform: 'Windows',
        subtitle: 'Primary shipping lane',
        configured: true,
        details: 'Current packaged validation is strongest on the Windows Unreal lane.',
      },
      {
        platformKey: 'macos',
        platform: 'macOS',
        subtitle: 'Planned distribution surface',
        configured: false,
        details: 'List a signed desktop build here when the package lane is opened.',
      },
      {
        platformKey: 'linux',
        platform: 'Linux',
        subtitle: 'Operator-targeted later lane',
        configured: false,
        details: 'Use for future package publication after the bounded release lane is widened.',
      },
    ],
  }
})

import { DownloadPage } from '../pages/public-pages'
import { LoginPage, RegisterPage } from '../pages/auth-pages'

function LocationEcho() {
  const location = useLocation()
  return <div data-testid="location">{location.pathname}{location.search}</div>
}

function renderAuthRoutes(initialEntry: string) {
  return render(
    <MemoryRouter initialEntries={[initialEntry]} future={ROUTER_FUTURE_FLAGS}>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        <Route path="/register" element={<RegisterPage />} />
        <Route path="/app" element={<LocationEcho />} />
        <Route path="/app/downloads" element={<LocationEcho />} />
      </Routes>
    </MemoryRouter>,
  )
}

function renderDownloadPage() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  })

  return render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter future={ROUTER_FUTURE_FLAGS}>
        <DownloadPage />
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

describe('public auth and download pages', () => {
  beforeEach(() => {
    cleanup()
    mockLogin.mockReset()
    mockRegister.mockReset()
    mockUsePlatformAuth.mockReset()
    mockGetSuperTokensAuthRuntimeValidation.mockReset()
    mockIsGitHubOAuthEnabled.mockReset()
    mockIsGoogleOAuthEnabled.mockReset()
    mockIsOrcidOAuthEnabled.mockReset()
    mockGetAuthHealth.mockReset()
    mockGetReleaseManifest.mockReset()
    mockGetSuperTokensAuthRuntimeValidation.mockReturnValue({
      ready: true,
      missing: [],
      warnings: [],
    })
    mockIsGitHubOAuthEnabled.mockReturnValue(false)
    mockIsGoogleOAuthEnabled.mockReturnValue(false)
    mockIsOrcidOAuthEnabled.mockReturnValue(false)
    mockUsePlatformAuth.mockReturnValue({
      isAuthenticated: false,
      login: (...args: unknown[]) => mockLogin(...args),
      register: (...args: unknown[]) => mockRegister(...args),
    })
    mockGetAuthHealth.mockResolvedValue({
      ok: true,
      service: 'hypertwist-auth-server',
      supertokens: {
        configured: true,
        reachable: true,
        ready: true,
        apiVersion: '5.4',
        error: null,
        oauth: {
          github: false,
          google: false,
        },
      },
      fallback: {
        enabled: true,
        active: false,
        reason: null,
      },
      billing: {
        statePath: '/var/lib/hypertwist/auth/hypertwist-billing-state.json',
        processedEventCount: 0,
        pricePlanMapConfigured: false,
        productPlanMapConfigured: false,
        webhookSecretConfigured: false,
      },
      runtime: {
        mode: 'mixed',
        public_origin_ready: true,
        cookie_secure: true,
        api_domain: 'https://hypertwist.app',
        website_domain: 'https://hypertwist.app',
        warnings: [],
        errors: [],
      },
    })
    mockGetReleaseManifest.mockResolvedValue({
      ok: true,
      manifest: {
        generated_at: '2026-06-22T12:00:00.000Z',
        support_email: 'hello@hypertwist.app',
        public_docs_url: 'https://docs.hypertwist.app',
        release_notes_url: 'https://notes.hypertwist.app',
        corresponding_source_url: 'https://hypertwist.app/open-source/source.zip',
        open_source_repo_url: 'https://github.com/hypertwist/hypertwist',
        viewer: {
          authenticated: false,
          canDownload: false,
          plan: null,
          role: null,
          accessStatus: null,
        },
        platforms: [
          {
            platform_key: 'windows',
            platform: 'Windows',
            subtitle: 'Primary shipping lane',
            details: 'Current packaged validation is strongest on the Windows Unreal lane.',
            configured: true,
            channel: 'candidate',
            version: '1.0.0',
            build_id: 'win64-1000',
            published_at: '2026-06-22T00:00:00.000Z',
            file_name: 'HyperTwist-Windows.zip',
            file_size_bytes: 1048576,
            checksum_sha256: 'abc123',
            download_url: null,
            download_available: false,
          },
        ],
      },
    })
  })

  it('falls back unsafe login next targets to /app and preserves that safe fallback in the register link', async () => {
    mockLogin.mockResolvedValue({ ok: true })

    renderAuthRoutes('/login?next=%2F%2Fevil.example')

    const registerLink = screen.getByRole('link', { name: /create an account/i })
    expect(registerLink.getAttribute('href')).toBe('/register?next=%2Fapp')
    expect(screen.getAllByText('Signed-in dashboard').length).toBeGreaterThan(0)
    expect(screen.getAllByText(/After sign-in you will continue to the signed-in dashboard/i).length).toBeGreaterThan(0)
    expect(screen.getByText('Browser account access does not replace the simulator.')).toBeTruthy()
    expect(screen.getByText('Controls and device support today')).toBeTruthy()
    expect(screen.getAllByText('Signed-in dashboard').length).toBeGreaterThan(0)
    expect(screen.getAllByText('Desktop simulator').length).toBeGreaterThan(0)
    expect(screen.getByText('What the desktop runtime is for once you are signed in')).toBeTruthy()
    expect(screen.getByText('1. Run a classic-cube training session')).toBeTruthy()

    await userEvent.type(screen.getByLabelText('Email'), 'operator@hypertwist.app')
    await userEvent.type(screen.getByLabelText('Password'), 'password123')
    await userEvent.click(screen.getByRole('button', { name: 'Log in' }))

    expect((await screen.findByTestId('location')).textContent).toBe('/app')
    expect(mockLogin).toHaveBeenCalledWith({
      method: 'email',
      email: 'operator@hypertwist.app',
      password: 'password123',
    })
  })

  it('preserves safe register next targets across footer links and successful account creation', async () => {
    mockRegister.mockResolvedValue({ ok: true })

    renderAuthRoutes('/register?next=%2Fapp%2Fdownloads%3Fplatform%3Dwindows')

    const loginLink = screen.getByRole('link', { name: 'Log in' })
    expect(loginLink.getAttribute('href')).toBe('/login?next=%2Fapp%2Fdownloads%3Fplatform%3Dwindows')
    expect(screen.getByText('Signed-in Windows downloads')).toBeTruthy()
    expect(screen.getAllByText(/After sign-in you will continue to the signed-in Windows download center/i).length).toBeGreaterThan(0)
    expect(screen.getByText('Browser account access does not replace the simulator.')).toBeTruthy()
    expect(screen.getByText('Why the website feels fast and focused')).toBeTruthy()
    expect(screen.getAllByText('Signed-in dashboard').length).toBeGreaterThan(0)
    expect(screen.getAllByText('Desktop simulator').length).toBeGreaterThan(0)
    expect(screen.getByText('What the desktop runtime is for once you are signed in')).toBeTruthy()
    expect(screen.getByText('2. Run a recognition and correction workflow')).toBeTruthy()

    await userEvent.type(screen.getByLabelText('Name'), 'Operator')
    await userEvent.type(screen.getByLabelText('Email'), 'operator@hypertwist.app')
    await userEvent.type(screen.getByLabelText('Password'), 'password123')
    await userEvent.click(screen.getByRole('button', { name: 'Create account' }))

    expect((await screen.findByTestId('location')).textContent).toBe('/app/downloads?platform=windows')
    expect(mockRegister).toHaveBeenCalledWith({
      method: 'email',
      email: 'operator@hypertwist.app',
      password: 'password123',
      name: 'Operator',
    })
  })

  it('shows login errors without navigating away from the auth page', async () => {
    mockLogin.mockResolvedValue({ ok: false, error: 'Incorrect email or password.' })

    renderAuthRoutes('/login?next=%2Fapp')

    await userEvent.type(screen.getByLabelText('Email'), 'operator@hypertwist.app')
    await userEvent.type(screen.getByLabelText('Password'), 'wrong-password')
    await userEvent.click(screen.getByRole('button', { name: 'Log in' }))

    expect(await screen.findByText('Incorrect email or password.')).toBeTruthy()
    expect(screen.queryByTestId('location')).toBeNull()
  })

  it('shows register errors without navigating away from the auth page', async () => {
    mockRegister.mockResolvedValue({ ok: false, error: 'Sign-up is not allowed right now.' })

    renderAuthRoutes('/register?next=%2Fapp%2Fdownloads')

    await userEvent.type(screen.getByLabelText('Name'), 'Operator')
    await userEvent.type(screen.getByLabelText('Email'), 'operator@hypertwist.app')
    await userEvent.type(screen.getByLabelText('Password'), 'password123')
    await userEvent.click(screen.getByRole('button', { name: 'Create account' }))

    expect(await screen.findByText('Sign-up is not allowed right now.')).toBeTruthy()
    expect(screen.queryByTestId('location')).toBeNull()
  })

  it('surfaces auth-runtime missing settings and recovery guidance on the login page', () => {
    mockGetSuperTokensAuthRuntimeValidation.mockReturnValue({
      ready: false,
      missing: ['VITE_SUPERTOKENS_API_DOMAIN'],
      warnings: ['VITE_AUTH_API_BASE_URL not configured; same-origin auth fallback remains active.'],
    })

    renderAuthRoutes('/login')

    expect(screen.getByText(/Account access can continue through the available browser-auth lane./i)).toBeTruthy()
    expect(screen.getByText('What still works')).toBeTruthy()
    expect(screen.getByText('What may still need follow-through')).toBeTruthy()
    expect(screen.getByText('Best next step')).toBeTruthy()
    expect(screen.getByText('Auth runtime setting pending: VITE_SUPERTOKENS_API_DOMAIN')).toBeTruthy()
    expect(screen.getByText('VITE_AUTH_API_BASE_URL not configured; same-origin auth fallback remains active.')).toBeTruthy()
    expect(screen.getByText('Browser account access does not replace the simulator.')).toBeTruthy()
    expect(screen.getByRole('link', { name: 'Open support' }).getAttribute('href')).toBe('/support?topic=operator-access')
    expect(screen.getAllByText('Signed-in dashboard').length).toBeGreaterThan(0)
    expect(screen.getByText('What the desktop runtime is for once you are signed in')).toBeTruthy()
  })

  it('shows enabled OAuth buttons and routes provider clicks through the login handler', async () => {
    mockIsGoogleOAuthEnabled.mockReturnValue(true)
    mockIsGitHubOAuthEnabled.mockReturnValue(true)
    mockIsOrcidOAuthEnabled.mockReturnValue(true)
    mockLogin.mockResolvedValue({ ok: true })

    renderAuthRoutes('/login')

    await userEvent.click(screen.getByRole('button', { name: 'Continue with Google' }))
    await userEvent.click(screen.getByRole('button', { name: 'Continue with GitHub' }))
    await userEvent.click(screen.getByRole('button', { name: 'Continue with ORCID' }))

    expect(mockLogin).toHaveBeenCalledWith({ method: 'google', email: '' })
    expect(mockLogin).toHaveBeenCalledWith({ method: 'github', email: '' })
    expect(mockLogin).toHaveBeenCalledWith({ method: 'orcid', email: '' })
  })

  it('shows the same enabled OAuth providers on the register page and routes them through the shared login handler', async () => {
    mockIsGoogleOAuthEnabled.mockReturnValue(true)
    mockIsGitHubOAuthEnabled.mockReturnValue(true)
    mockIsOrcidOAuthEnabled.mockReturnValue(true)
    mockLogin.mockResolvedValue({ ok: true })

    renderAuthRoutes('/register')

    await userEvent.click(screen.getByRole('button', { name: 'Continue with Google' }))
    await userEvent.click(screen.getByRole('button', { name: 'Continue with GitHub' }))
    await userEvent.click(screen.getByRole('button', { name: 'Continue with ORCID' }))

    expect(mockLogin).toHaveBeenCalledWith({ method: 'google', email: '' })
    expect(mockLogin).toHaveBeenCalledWith({ method: 'github', email: '' })
    expect(mockLogin).toHaveBeenCalledWith({ method: 'orcid', email: '' })
  })

  it('keeps configured public download targets behind the protected dashboard instead of exposing raw URLs', async () => {
    renderDownloadPage()

    const windowsAccessLink = screen.getByRole('link', { name: /sign in for windows access/i })
    expect(windowsAccessLink.getAttribute('href')).toBe('/app/downloads?platform=windows')
    expect(screen.queryByText('https://downloads.hypertwist.app/windows.exe')).toBeNull()
    expect(await screen.findByText('Version: 1.0.0')).toBeTruthy()
    expect(document.title).toBe('Download HyperTwist desktop | HyperTwist')
    expect(document.head.querySelector('meta[property="og:url"]')?.getAttribute('content')).toBe('https://hypertwist.app/download')
  })

  it('marks browser auth pages as noindex while keeping route-aware titles', () => {
    renderAuthRoutes('/login')

    expect(document.title).toBe('Log in to HyperTwist | HyperTwist')
    expect(document.head.querySelector('meta[name="robots"]')?.getAttribute('content')).toBe('noindex,nofollow')
  })
})
