import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import type { RouteLocationNormalized } from 'vue-router'
import { useAuthStore, getDefaultRouteForRole } from '@/stores/auth'

vi.mock('@/api/client', () => ({
  post: vi.fn(),
  get: vi.fn(),
}))

function buildRoute(path: string, meta: Record<string, unknown> = {}): RouteLocationNormalized {
  return {
    path,
    meta,
    name: undefined,
    params: {},
    query: {},
    hash: '',
    fullPath: path,
    matched: [],
    redirectedFrom: undefined,
  }
}

function setupGuard() {
  const nextCalls: (string | undefined)[] = []
  const auth = useAuthStore()

  function runGuard(to: RouteLocationNormalized) {
    const next = vi.fn((dest?: string) => {
      nextCalls.push(dest)
    })

    if (to.path === '/login' && auth.isAuthenticated) {
      next(getDefaultRouteForRole(auth.userRole))
      return { next, nextCalls }
    }

    if (to.meta.requiresAuth && !auth.isAuthenticated) {
      next('/login')
      return { next, nextCalls }
    }

    if (to.meta.roles && Array.isArray(to.meta.roles)) {
      const allowedRoles = to.meta.roles as string[]
      if (!allowedRoles.includes(auth.userRole)) {
        const fallback = getDefaultRouteForRole(auth.userRole)
        if (fallback !== '/login' && fallback !== to.path) {
          next(fallback)
          return { next, nextCalls }
        }
        next('/login')
        return { next, nextCalls }
      }
    }

    next()
    return { next, nextCalls }
  }

  return { auth, runGuard }
}

beforeEach(() => {
  setActivePinia(createPinia())
  vi.clearAllMocks()
})

describe('router navigation guard', () => {
  describe('unauthenticated users', () => {
    it('allows access to /login', () => {
      const { runGuard } = setupGuard()
      const { next } = runGuard(buildRoute('/login', { requiresAuth: false }))
      expect(next).toHaveBeenCalledWith()
    })

    it('redirects to /login for protected routes', () => {
      const { runGuard } = setupGuard()
      const { next } = runGuard(buildRoute('/entry', { requiresAuth: true, roles: ['DATA_ENTRY_CLERK'] }))
      expect(next).toHaveBeenCalledWith('/login')
    })

    it('redirects to /login for dashboard', () => {
      const { runGuard } = setupGuard()
      const { next } = runGuard(buildRoute('/dashboard', { requiresAuth: true, roles: ['ADMINISTRATOR'] }))
      expect(next).toHaveBeenCalledWith('/login')
    })
  })

  describe('authenticated users', () => {
    function authenticatedGuard(role: string) {
      const { auth, runGuard } = setupGuard()
      auth.token = 'valid-token'
      auth.user = { id: 'u1', username: 'test', fullName: 'Test User', role }
      return { auth, runGuard }
    }

    it('redirects authenticated user away from /login to default route', () => {
      const { runGuard } = authenticatedGuard('DATA_ENTRY_CLERK')
      const { next } = runGuard(buildRoute('/login', { requiresAuth: false }))
      expect(next).toHaveBeenCalledWith('/entry')
    })

    it('redirects admin from /login to /dashboard', () => {
      const { runGuard } = authenticatedGuard('ADMINISTRATOR')
      const { next } = runGuard(buildRoute('/login', { requiresAuth: false }))
      expect(next).toHaveBeenCalledWith('/dashboard')
    })

    it('allows access to routes matching user role', () => {
      const { runGuard } = authenticatedGuard('DATA_ENTRY_CLERK')
      const { next } = runGuard(buildRoute('/entry', { requiresAuth: true, roles: ['DATA_ENTRY_CLERK', 'ADMINISTRATOR'] }))
      expect(next).toHaveBeenCalledWith()
    })

    it('allows admin access to any role-restricted route', () => {
      const { runGuard } = authenticatedGuard('ADMINISTRATOR')
      const routeMetas = [
        { requiresAuth: true, roles: ['INTAKE_CLERK', 'ADMINISTRATOR'] },
        { requiresAuth: true, roles: ['DATA_ENTRY_CLERK', 'ADMINISTRATOR'] },
        { requiresAuth: true, roles: ['VERIFIER', 'CLINICAL_APPROVER', 'ADMINISTRATOR'] },
        { requiresAuth: true, roles: ['CLINICAL_APPROVER', 'ADMINISTRATOR'] },
        { requiresAuth: true, roles: ['CLINICIAN', 'ADMINISTRATOR'] },
        { requiresAuth: true, roles: ['ADMINISTRATOR'] },
      ]
      for (const meta of routeMetas) {
        const { next } = runGuard(buildRoute('/test', meta))
        expect(next).toHaveBeenCalledWith()
      }
    })

    it('allows INTAKE_CLERK access to cover sheets route', () => {
      const { runGuard } = authenticatedGuard('INTAKE_CLERK')
      const { next } = runGuard(buildRoute('/cover-sheets', {
        requiresAuth: true,
        roles: ['INTAKE_CLERK', 'ADMINISTRATOR'],
      }))
      expect(next).toHaveBeenCalledWith()
    })

    it('allows ADMINISTRATOR access to fhir-explorer route', () => {
      const { runGuard } = authenticatedGuard('ADMINISTRATOR')
      const { next } = runGuard(buildRoute('/fhir-explorer', {
        requiresAuth: true,
        roles: ['ADMINISTRATOR'],
      }))
      expect(next).toHaveBeenCalledWith()
    })

    it('redirects DATA_ENTRY_CLERK from fhir-explorer to /entry', () => {
      const { runGuard } = authenticatedGuard('DATA_ENTRY_CLERK')
      const { next } = runGuard(buildRoute('/fhir-explorer', {
        requiresAuth: true,
        roles: ['ADMINISTRATOR'],
      }))
      expect(next).toHaveBeenCalledWith('/entry')
    })

    it('redirects DATA_ENTRY_CLERK from cover sheets to /entry', () => {
      const { runGuard } = authenticatedGuard('DATA_ENTRY_CLERK')
      const { next } = runGuard(buildRoute('/cover-sheets', {
        requiresAuth: true,
        roles: ['INTAKE_CLERK', 'ADMINISTRATOR'],
      }))
      expect(next).toHaveBeenCalledWith('/entry')
    })

    it('redirects DATA_ENTRY_CLERK from /intake to /entry', () => {
      const { runGuard } = authenticatedGuard('DATA_ENTRY_CLERK')
      const { next } = runGuard(buildRoute('/intake', { requiresAuth: true, roles: ['INTAKE_CLERK', 'ADMINISTRATOR'] }))
      expect(next).toHaveBeenCalledWith('/entry')
    })

    it('redirects INTAKE_CLERK from /verification to /intake', () => {
      const { runGuard } = authenticatedGuard('INTAKE_CLERK')
      const { next } = runGuard(buildRoute('/verification', {
        requiresAuth: true,
        roles: ['VERIFIER', 'CLINICAL_APPROVER', 'ADMINISTRATOR'],
      }))
      expect(next).toHaveBeenCalledWith('/intake')
    })

    it('redirects CLINICIAN from /dashboard to /live-capture', () => {
      const { runGuard } = authenticatedGuard('CLINICIAN')
      const { next } = runGuard(buildRoute('/dashboard', { requiresAuth: true, roles: ['ADMINISTRATOR'] }))
      expect(next).toHaveBeenCalledWith('/live-capture')
    })

    it('allows access to routes with no role restriction', () => {
      const { runGuard } = authenticatedGuard('DATA_ENTRY_CLERK')
      const { next } = runGuard(buildRoute('/patients', { requiresAuth: true }))
      expect(next).toHaveBeenCalledWith()
    })

    it('VERIFIER can access verification routes', () => {
      const { runGuard } = authenticatedGuard('VERIFIER')
      const { next } = runGuard(buildRoute('/verification', {
        requiresAuth: true,
        roles: ['VERIFIER', 'CLINICAL_APPROVER', 'ADMINISTRATOR'],
      }))
      expect(next).toHaveBeenCalledWith()
    })

    it('CLINICAL_APPROVER can access both verification and approval routes', () => {
      const { runGuard } = authenticatedGuard('CLINICAL_APPROVER')

      const { next: verifyNext } = runGuard(buildRoute('/verification', {
        requiresAuth: true,
        roles: ['VERIFIER', 'CLINICAL_APPROVER', 'ADMINISTRATOR'],
      }))
      expect(verifyNext).toHaveBeenCalledWith()

      const { next: approvalNext } = runGuard(buildRoute('/approval', {
        requiresAuth: true,
        roles: ['CLINICAL_APPROVER', 'ADMINISTRATOR'],
      }))
      expect(approvalNext).toHaveBeenCalledWith()
    })
  })
})
