import type { Request, Response, NextFunction } from 'express'
import {
  requireAdminRole,
  verifyAdminSessionToken,
} from '../../services/admin/admin-auth-service.js'
import { writeAdminAuditLog } from '../../services/admin/admin-audit-service.js'
import { buildSuccessPayload, createHttpError, sendRouteError } from '../../utils/http.js'
import { logWarn } from '../../utils/logger.js'
import type { AdminSession } from '../../services/admin/admin-auth-service.js'

type AdminAuditPayload = {
  action?: string
  targetType?: string
  targetId?: string
  data?: Record<string, unknown>
}

type AdminJsonHandlerOptions = {
  successMessage?: string
  errorMessage?: string
  scope?: string
  audit?: (req: Request, data: unknown) => AdminAuditPayload | null | undefined
}

type AdminFileHandlerOptions = {
  errorMessage?: string
  scope?: string
}

export function createJsonHandler(
  action: (req: Request, res: Response) => unknown | Promise<unknown>,
  { successMessage = 'ok', errorMessage, scope, audit }: AdminJsonHandlerOptions = {},
) {
  return async (req: Request, res: Response): Promise<void> => {
    try {
      const data = await action(req, res)
      await recordAdminAudit(req.adminSession, req, data, audit)
      res.json(buildSuccessPayload(data, successMessage))
    } catch (error) {
      sendRouteError(res, error, errorMessage, scope)
    }
  }
}

export function createFileHandler(
  action: (req: Request, res: Response) => string | Promise<string>,
  { errorMessage, scope }: AdminFileHandlerOptions = {},
) {
  return async (req: Request, res: Response): Promise<void> => {
    try {
      const filePath = await action(req, res)
      res.sendFile(filePath)
    } catch (error) {
      sendRouteError(res, error, errorMessage, scope)
    }
  }
}

export async function requireAdminSession(
  req: Request,
  res: Response,
  next: NextFunction,
): Promise<void> {
  try {
    req.adminSession = await verifyAdminSessionToken(extractBearerToken(req))
    next()
  } catch (error) {
    sendRouteError(res, error, '后台鉴权失败', '[admin/auth]')
  }
}

export function requireAdminRoles(allowedRoles: string[]) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      requireAdminRole(req.adminSession, allowedRoles)
      next()
    } catch (error) {
      sendRouteError(res, error, '后台鉴权失败', '[admin/auth/role]')
    }
  }
}

export function requireAdminPermission(permission: string) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      const session = getRequiredAdminSession(req)
      if (!session.permissions.includes(String(permission || '').trim())) {
        throw createHttpError('当前账号没有此操作权限', {
          statusCode: 403,
          errorCode: 'admin_permission_denied',
        })
      }
      next()
    } catch (error) {
      sendRouteError(res, error, '后台鉴权失败', '[admin/auth/permission]')
    }
  }
}

export function requireAnyAdminPermission(permissions: string[]) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      const session = getRequiredAdminSession(req)
      if (
        permissions.some((permission) =>
          session.permissions.includes(String(permission || '').trim()),
        )
      ) {
        next()
        return
      }
      throw createHttpError('当前账号没有此操作权限', {
        statusCode: 403,
        errorCode: 'admin_permission_denied',
      })
    } catch (error) {
      sendRouteError(res, error, '后台鉴权失败', '[admin/auth/permission]')
    }
  }
}

export function getRequiredAdminSession(req: Request): AdminSession {
  if (req.adminSession) {
    return req.adminSession
  }

  throw createHttpError('未登录或登录已失效', {
    statusCode: 401,
    errorCode: 'admin_auth_required',
  })
}

async function recordAdminAudit(
  session: AdminSession | null | undefined,
  req: Request,
  data: unknown,
  audit: AdminJsonHandlerOptions['audit'],
) {
  if (!audit) {
    return
  }

  const payload = typeof audit === 'function' ? audit(req, data) : audit

  if (!payload) {
    return
  }

  try {
    await writeAdminAuditLog(session, payload)
  } catch (error) {
    logWarn('[admin/audit]', '后台审计日志写入失败，已跳过', {
      action: payload.action,
      targetType: payload.targetType,
      targetId: payload.targetId,
      error,
    })
  }
}

export function extractBearerToken(req: Request): string {
  const authorization = String(req.headers.authorization || '').trim()
  const matched = authorization.match(/^Bearer\s+(.+)$/i)

  if (!matched) {
    throw createHttpError('未登录或登录已失效', {
      statusCode: 401,
      errorCode: 'admin_auth_required',
    })
  }

  const token = matched[1]?.trim()
  if (!token) {
    throw createHttpError('未登录或登录已失效', {
      statusCode: 401,
      errorCode: 'admin_auth_required',
    })
  }

  return token
}
