import { NextApiRequest, NextApiResponse, NextApiHandler } from 'next'
import { getServerSession } from 'next-auth/next'
import { authOptions } from '@/pages/api/auth/[...nextauth]'
import { UserRole, AuthUser } from './types'
import { hasPermission, hasRole } from './permissions'

export interface AuthenticatedRequest extends NextApiRequest {
  user: AuthUser
}

type AuthenticatedHandler = (
  req: AuthenticatedRequest,
  res: NextApiResponse
) => Promise<void> | void

interface WithAuthOptions {
  requiredRole?: UserRole
  requiredPermission?: string
  requiredPermissions?: string[]
  requireAll?: boolean // If true, requires all permissions; if false, requires any
}

export function withAuth(
  handler: AuthenticatedHandler,
  options?: WithAuthOptions
): NextApiHandler {
  return async (req: NextApiRequest, res: NextApiResponse) => {
    try {
      const session = await getServerSession(req, res, authOptions)

      if (!session?.user) {
        return res.status(401).json({
          error: 'Unauthorized',
          message: 'You must be signed in to access this resource',
        })
      }

      const user: AuthUser = {
        id: session.user.id,
        email: session.user.email!,
        name: session.user.name,
        image: session.user.image,
        role: session.user.role || UserRole.USER,
        emailVerified: session.user.emailVerified,
      }

      // Check role requirement
      if (options?.requiredRole) {
        if (!hasRole(user.role, options.requiredRole)) {
          return res.status(403).json({
            error: 'Forbidden',
            message: `This resource requires ${options.requiredRole} role or higher`,
          })
        }
      }

      // Check single permission
      if (options?.requiredPermission) {
        if (!hasPermission(user.role, options.requiredPermission)) {
          return res.status(403).json({
            error: 'Forbidden',
            message: `You do not have the required permission: ${options.requiredPermission}`,
          })
        }
      }

      // Check multiple permissions
      if (options?.requiredPermissions && options.requiredPermissions.length > 0) {
        const checkFunction = options.requireAll
          ? (perms: string[]) => perms.every(p => hasPermission(user.role, p))
          : (perms: string[]) => perms.some(p => hasPermission(user.role, p))

        if (!checkFunction(options.requiredPermissions)) {
          return res.status(403).json({
            error: 'Forbidden',
            message: options.requireAll
              ? `You need all of these permissions: ${options.requiredPermissions.join(', ')}`
              : `You need at least one of these permissions: ${options.requiredPermissions.join(', ')}`,
          })
        }
      }

      // Add user to request
      const authReq = req as AuthenticatedRequest
      authReq.user = user

      return handler(authReq, res)
    } catch (error) {
      console.error('Auth middleware error:', error)
      return res.status(500).json({
        error: 'Internal Server Error',
        message: 'An error occurred while authenticating your request',
      })
    }
  }
}

// Convenience wrapper for role-based protection
export function withRole(
  handler: AuthenticatedHandler,
  role: UserRole
): NextApiHandler {
  return withAuth(handler, { requiredRole: role })
}

// Convenience wrapper for permission-based protection
export function withPermission(
  handler: AuthenticatedHandler,
  permission: string
): NextApiHandler {
  return withAuth(handler, { requiredPermission: permission })
}

// Convenience wrapper for multiple permissions (any)
export function withAnyPermission(
  handler: AuthenticatedHandler,
  permissions: string[]
): NextApiHandler {
  return withAuth(handler, { requiredPermissions: permissions, requireAll: false })
}

// Convenience wrapper for multiple permissions (all)
export function withAllPermissions(
  handler: AuthenticatedHandler,
  permissions: string[]
): NextApiHandler {
  return withAuth(handler, { requiredPermissions: permissions, requireAll: true })
}

// Rate limiting helper (basic implementation)
const rateLimitMap = new Map<string, { count: number; resetAt: number }>()

export function checkRateLimit(
  identifier: string,
  limit: number = 100,
  windowMs: number = 60000
): { allowed: boolean; remaining: number; resetAt: number } {
  const now = Date.now()
  const record = rateLimitMap.get(identifier)

  if (!record || now > record.resetAt) {
    rateLimitMap.set(identifier, { count: 1, resetAt: now + windowMs })
    return { allowed: true, remaining: limit - 1, resetAt: now + windowMs }
  }

  if (record.count >= limit) {
    return { allowed: false, remaining: 0, resetAt: record.resetAt }
  }

  record.count++
  return { allowed: true, remaining: limit - record.count, resetAt: record.resetAt }
}

export function withRateLimit(
  handler: NextApiHandler,
  options: { limit?: number; windowMs?: number; keyGenerator?: (req: NextApiRequest) => string }
): NextApiHandler {
  const limit = options.limit || 100
  const windowMs = options.windowMs || 60000
  const keyGenerator = options.keyGenerator || ((req) => req.socket.remoteAddress || 'unknown')

  return async (req: NextApiRequest, res: NextApiResponse) => {
    const key = keyGenerator(req)
    const { allowed, remaining, resetAt } = checkRateLimit(key, limit, windowMs)

    res.setHeader('X-RateLimit-Limit', limit)
    res.setHeader('X-RateLimit-Remaining', remaining)
    res.setHeader('X-RateLimit-Reset', Math.ceil(resetAt / 1000))

    if (!allowed) {
      return res.status(429).json({
        error: 'Too Many Requests',
        message: 'Rate limit exceeded. Please try again later.',
        retryAfter: Math.ceil((resetAt - Date.now()) / 1000),
      })
    }

    return handler(req, res)
  }
}
