'use client'

import { usePathname, useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import { apiFetch } from '@/lib/api'
import { buildHomepageSignInPath, toDashboardAppPath, toPublicDashboardPath } from '@/lib/dashboardPaths'
import {
  getDashboardFallbackRoute,
  resolveDashboardRoutePolicy,
  roleCanAccessPolicy,
} from '@/lib/dashboardRoutePolicies'

type GeneratedMenuItem = {
  id: string
  itemType: 'INTERNAL_PAGE' | 'EXTERNAL_LINK' | 'PARENT_MENU' | 'SECTION_LABEL' | 'DIVIDER'
  routeOrUrl: string | null
  children: GeneratedMenuItem[]
}

type EmployeeMenuResponse = {
  items: GeneratedMenuItem[]
  subscriptionAccessLevel?: 'full' | 'limited' | 'read_only' | 'none'
}

type EmployeeProfileResponse = {
  employee: {
    role: string
  }
}

const ROLE_RANK: Record<string, number> = { OWNER: 3, MANAGER: 2, AGENT: 1 }
const BASELINE_MENU_ROUTES = [
  { route: '/', minRole: 'AGENT' },
  { route: '/reservations', minRole: 'AGENT' },
  { route: '/contracts', minRole: 'AGENT' },
  { route: '/fleet', minRole: 'AGENT' },
  { route: '/customers', minRole: 'AGENT' },
  { route: '/reports', minRole: 'MANAGER' },
  { route: '/billing', minRole: 'MANAGER' },
  { route: '/settings', minRole: 'OWNER' },
] as const

const MENU_RECOVERY_ROUTES = new Set(['/subscription', '/subscription/success', '/subscription/cancel'])

function hasMinRole(employeeRole: string, minRole: string): boolean {
  return (ROLE_RANK[employeeRole] ?? 0) >= (ROLE_RANK[minRole] ?? 0)
}

export function getBaselineInternalRoutes(role: string): string[] {
  return BASELINE_MENU_ROUTES
    .filter((item) => hasMinRole(role, item.minRole))
    .map((item) => item.route)
}

export function flattenInternalRoutes(items: GeneratedMenuItem[]): string[] {
  const routes: string[] = []

  const walk = (entry: GeneratedMenuItem) => {
    if (entry.itemType === 'INTERNAL_PAGE' && entry.routeOrUrl) {
      routes.push(toDashboardAppPath(entry.routeOrUrl))
    }
    entry.children.forEach(walk)
  }

  items.forEach(walk)
  return Array.from(new Set(routes))
}

export function resolveAllowedRoutes(menu: EmployeeMenuResponse, role: string): string[] {
  const routes = flattenInternalRoutes(menu.items)
  const featureRoutes = routes.filter((route) => route !== '/' && !MENU_RECOVERY_ROUTES.has(route))
  const shouldUseBaselineFallback =
    menu.subscriptionAccessLevel !== 'none' &&
    featureRoutes.length === 0

  const resolvedRoutes = shouldUseBaselineFallback ? getBaselineInternalRoutes(role) : routes
  if (resolvedRoutes.includes('/reservations') && !resolvedRoutes.includes('/contracts')) {
    return [...resolvedRoutes, '/contracts']
  }
  return resolvedRoutes
}

export function isAllowedRoute(currentPath: string, allowedRoutes: string[]) {
  return allowedRoutes.some((route) => {
    if (route === '/') return currentPath === '/'
    return currentPath === route || currentPath.startsWith(`${route}/`)
  })
}

export function resolveAccessRedirect(currentPath: string, allowedRoutes: string[]) {
  if (allowedRoutes.length === 0) return null
  if (isAllowedRoute(currentPath, allowedRoutes)) return null

  const fallbackRoute = allowedRoutes[0] ?? '/'
  return fallbackRoute === currentPath ? null : fallbackRoute
}

export function buildSignInRedirect(currentPath: string) {
  return buildHomepageSignInPath(currentPath)
}

export default function DashboardAccessGuard({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  const router = useRouter()
  const [ready, setReady] = useState(false)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false

    async function enforceAccess() {
      const currentPath = toDashboardAppPath(pathname)
      const policy = resolveDashboardRoutePolicy(currentPath)
      try {
        setReady(false)
        setError(null)

        const { employee } = await apiFetch<EmployeeProfileResponse>('/auth/employee/me')
        if (cancelled) return

        if (!roleCanAccessPolicy(employee.role, policy)) {
          const fallbackRoute = getDashboardFallbackRoute(employee.role)
          if (fallbackRoute !== currentPath) {
            router.replace(fallbackRoute)
            return
          }
        }

        if (policy.menuRegistrationRequired) {
          const menu = await apiFetch<EmployeeMenuResponse>('/auth/employee/menu')
          if (cancelled) return

          if (menu.subscriptionAccessLevel === 'none') {
            router.replace('/subscription')
            return
          }

          const allowedRoutes = resolveAllowedRoutes(menu, employee.role)
          const redirectPath = resolveAccessRedirect(currentPath, allowedRoutes)

          if (redirectPath) {
            router.replace(redirectPath)
            return
          }
        }

        setReady(true)
      } catch (error: any) {
        if (cancelled) return

        if (error?.statusCode === 401) {
          const target = buildSignInRedirect(currentPath)
          if (target !== toPublicDashboardPath(currentPath)) window.location.replace(target)
          return
        }

        if (error?.statusCode === 402) {
          if (policy.billingRecoveryRoute) setReady(true)
          else router.replace('/subscription')
          return
        }

        if (error?.statusCode === 403) {
          if (currentPath !== '/') router.replace('/')
          else setReady(true)
          return
        }

        setError(error?.message ?? 'Unable to verify dashboard access. Please try again.')
      }
    }

    enforceAccess()

    return () => {
      cancelled = true
    }
  }, [pathname, router])

  if (error) {
    return (
      <div className="flex min-h-[40vh] items-center justify-center px-6">
        <div className="max-w-md rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700">
          {error}
        </div>
      </div>
    )
  }

  if (!ready) {
    return (
      <div className="flex min-h-[40vh] items-center justify-center">
        <div className="h-6 w-6 animate-spin rounded-full border-2 border-orange-500 border-t-transparent" aria-label="Checking access" />
      </div>
    )
  }

  return <>{children}</>
}
