import { describe, it, expect, beforeEach, vi } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'

import type { RouteLocationNormalized, NavigationGuardNext } from 'vue-router'

import { useAuthStore } from '@/stores/auth'
import { requireAuth, requireGuest, requireStarter } from './guards'

// Helper to create mock route objects
function createMockRoute(overrides: Partial<RouteLocationNormalized> = {}): RouteLocationNormalized {
  return {
    path: '/',
    name: undefined,
    params: {},
    query: {},
    hash: '',
    fullPath: '/',
    matched: [],
    meta: {},
    redirectedFrom: undefined,
    ...overrides,
  }
}

describe('requireAuth', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('redirects to login when not authenticated', () => {
    /**
     * Test that unauthenticated users are redirected to login.
     *
     * Protected routes must not be accessible without authentication.
     * The redirect should include the original path so users can be
     * returned there after logging in.
     */
    const auth = useAuthStore()
    auth.token = null

    const to = createMockRoute({ path: '/collection', fullPath: '/collection' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireAuth(to, from, next)

    expect(next).toHaveBeenCalledWith({
      path: '/login',
      query: { redirect: '/collection' },
    })
  })

  it('allows access when authenticated', () => {
    /**
     * Test that authenticated users can access protected routes.
     *
     * When a user has a valid token, they should be allowed through
     * to the requested route without redirection.
     */
    const auth = useAuthStore()
    auth.token = 'valid-token'

    const to = createMockRoute({ path: '/collection' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireAuth(to, from, next)

    expect(next).toHaveBeenCalledWith()
  })
})

describe('requireGuest', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('redirects to home when authenticated', () => {
    /**
     * Test that authenticated users are redirected away from guest routes.
     *
     * Pages like /login shouldn't be accessible to logged-in users
     * since they don't need to log in again.
     */
    const auth = useAuthStore()
    auth.token = 'valid-token'

    const to = createMockRoute({ path: '/login' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireGuest(to, from, next)

    expect(next).toHaveBeenCalledWith({ path: '/' })
  })

  it('allows access when not authenticated', () => {
    /**
     * Test that unauthenticated users can access guest routes.
     *
     * Users who are not logged in should be able to reach the
     * login page and other guest-only routes.
     */
    const auth = useAuthStore()
    auth.token = null

    const to = createMockRoute({ path: '/login' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireGuest(to, from, next)

    expect(next).toHaveBeenCalledWith()
  })
})

describe('requireStarter', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('allows access to /starter without checking', () => {
    /**
     * Test that the starter page itself is always accessible.
     *
     * Users must be able to reach /starter to select their deck,
     * even if they haven't selected one yet (which would otherwise
     * cause a redirect loop).
     */
    const auth = useAuthStore()
    auth.token = 'valid-token'
    auth.user = { id: '1', displayName: 'Test', avatarUrl: null, hasStarterDeck: false }

    const to = createMockRoute({ path: '/starter' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireStarter(to, from, next)

    expect(next).toHaveBeenCalledWith()
  })

  it('redirects to /starter when user has no starter deck', () => {
    /**
     * Test that users without a starter deck are redirected.
     *
     * New users must select a starter deck before accessing the
     * main app. This ensures they have cards to play with.
     */
    const auth = useAuthStore()
    auth.token = 'valid-token'
    auth.user = { id: '1', displayName: 'Test', avatarUrl: null, hasStarterDeck: false }

    const to = createMockRoute({ path: '/collection' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireStarter(to, from, next)

    expect(next).toHaveBeenCalledWith({ path: '/starter' })
  })

  it('allows access when user has starter deck', () => {
    /**
     * Test that users with a starter deck can access the main app.
     *
     * Once a user has selected their starter deck, they should
     * have full access to collection, decks, and gameplay.
     */
    const auth = useAuthStore()
    auth.token = 'valid-token'
    auth.user = { id: '1', displayName: 'Test', avatarUrl: null, hasStarterDeck: true }

    const to = createMockRoute({ path: '/collection' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireStarter(to, from, next)

    expect(next).toHaveBeenCalledWith()
  })

  it('allows access when user is not loaded yet', () => {
    /**
     * Test that the guard doesn't block when user data is pending.
     *
     * If the user object hasn't been fetched yet (null), we should
     * allow the navigation to continue. The app will handle fetching
     * user data and may redirect later if needed.
     */
    const auth = useAuthStore()
    auth.token = 'valid-token'
    auth.user = null

    const to = createMockRoute({ path: '/collection' })
    const from = createMockRoute()
    const next = vi.fn() as unknown as NavigationGuardNext

    requireStarter(to, from, next)

    expect(next).toHaveBeenCalledWith()
  })
})
