import type { Request, Response, NextFunction } from 'express'
import { verifyToken, getTokenFromCookie } from './jwtProvider.js'
import type { JwtPayload } from './jwtProvider.js'
import { getUserInfo } from './authService.js'

declare global {
  namespace Express {
    interface Request {
      user?: JwtPayload
      resolvedPermissions?: Record<string, string[]>
    }
  }
}

export function requireAuth(req: Request, res: Response, next: NextFunction): void {
  const token = getTokenFromCookie(req.cookies || {})

  if (!token) {
    res.status(401).json({ error: '인증이 필요합니다.' })
    return
  }

  try {
    const payload = verifyToken(token)
    req.user = payload
    next()
  } catch {
    res.status(401).json({ error: '인증 토큰이 유효하지 않습니다.' })
  }
}

export function requireRole(...roles: string[]) {
  return (req: Request, res: Response, next: NextFunction): void => {
    if (!req.user) {
      res.status(401).json({ error: '인증이 필요합니다.' })
      return
    }

    const hasRole = req.user.roles.some((r) => roles.includes(r))
    if (!hasRole) {
      res.status(403).json({ error: '접근 권한이 없습니다.' })
      return
    }

    next()
  }
}

/**
 * 리소스 + 오퍼레이션 기반 권한 검사 미들웨어.
 *
 * OPER_CD는 HTTP Method가 아닌 비즈니스 의미로 결정한다.
 * 오퍼레이션 미지정 시 기본 'READ'.
 *
 * 사용 예:
 *   router.post('/notice/list',   requirePermission('board:notice', 'READ'),   handler)
 *   router.post('/notice/create', requirePermission('board:notice', 'CREATE'), handler)
 *   router.post('/notice/update', requirePermission('board:notice', 'UPDATE'), handler)
 *   router.post('/notice/delete', requirePermission('board:notice', 'DELETE'), handler)
 */
export function requirePermission(resource: string, operation: string = 'READ') {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    if (!req.user) {
      res.status(401).json({ error: '인증이 필요합니다.' })
      return
    }

    try {
      // req에 캐싱된 permissions 재사용 (요청당 1회만 DB 조회)
      if (!req.resolvedPermissions) {
        const userInfo = await getUserInfo(req.user.sub)
        req.resolvedPermissions = userInfo.permissions
      }

      // 정확한 리소스 매칭 → 부모 리소스 fallback (board:notice → board)
      let cursor: string | undefined = resource
      while (cursor) {
        const allowedOps = req.resolvedPermissions[cursor]
        if (allowedOps && allowedOps.includes(operation)) {
          next()
          return
        }
        const colonIdx = cursor.lastIndexOf(':')
        cursor = colonIdx > 0 ? cursor.substring(0, colonIdx) : undefined
      }

      res.status(403).json({ error: '접근 권한이 없습니다.' })
    } catch (err) {
      console.error('[auth] 권한 확인 오류:', err)
      res.status(500).json({ error: '권한 확인 중 오류가 발생했습니다.' })
    }
  }
}
