// 인증 미들웨어
// JWT 토큰 검증 및 사용자 정보 설정

import { Request, Response, NextFunction } from "express";
import { JwtUtils } from "../utils/jwtUtils";
import { AuthenticatedRequest, PersonBean } from "../types/auth";
import { logger } from "../utils/logger";

// AuthenticatedRequest 타입을 다른 모듈에서 사용할 수 있도록 re-export
export { AuthenticatedRequest } from "../types/auth";

// Express Request 타입 확장
declare global {
  namespace Express {
    interface Request {
      ip: string;
    }
  }
}

/**
 * JWT 토큰 검증 미들웨어
 * 기존 세션 방식과 동일한 효과를 제공
 */
export const authenticateToken = (
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): void => {
  try {
    // Authorization 헤더에서 토큰 추출
    const authHeader = req.get("Authorization");
    const token = authHeader && authHeader.split(" ")[1]; // Bearer TOKEN

    if (!token) {
      res.status(401).json({
        success: false,
        error: {
          code: "TOKEN_MISSING",
          details: "인증 토큰이 필요합니다.",
        },
      });
      return;
    }

    // JWT 토큰 검증 및 사용자 정보 추출
    const userInfo: PersonBean = JwtUtils.verifyToken(token);

    // 요청 객체에 사용자 정보 설정 (기존 PersonBean과 동일)
    req.user = userInfo;

    // 로그 기록
    logger.info(`인증 성공: ${userInfo.userId} (${req.ip})`);

    next();
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : "Unknown error";
    logger.error(`인증 실패: ${errorMessage} (${req.ip})`);

    // 토큰 만료 에러인지 확인
    const isTokenExpired = errorMessage.includes("만료");
    
    res.status(401).json({
      success: false,
      error: {
        code: isTokenExpired ? "TOKEN_EXPIRED" : "INVALID_TOKEN",
        details: errorMessage || "토큰 검증에 실패했습니다.",
      },
    });
  }
};

/**
 * 선택적 인증 미들웨어 (토큰이 없어도 통과)
 * 일부 API에서 사용 (예: 공개 정보 조회)
 */
export const optionalAuth = (
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): void => {
  try {
    const authHeader = req.get("Authorization");
    const token = authHeader && authHeader.split(" ")[1];

    if (token) {
      const userInfo: PersonBean = JwtUtils.verifyToken(token);
      req.user = userInfo;
      logger.debug(`선택적 인증 성공: ${userInfo.userId} (${req.ip})`);
    } else {
      logger.debug(`선택적 인증: 토큰 없음 (${req.ip})`);
    }

    next();
  } catch (error) {
    // 토큰이 있지만 유효하지 않은 경우에도 통과 (선택적 인증)
    logger.warn(
      `선택적 인증 실패: ${error instanceof Error ? error.message : "Unknown error"} (${req.ip})`
    );
    next();
  }
};

/**
 * 관리자 권한 확인 미들웨어
 */
export const requireAdmin = (
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): void => {
  if (!req.user) {
    res.status(401).json({
      success: false,
      error: {
        code: "AUTHENTICATION_REQUIRED",
        details: "인증이 필요합니다.",
      },
    });
    return;
  }

  // 기존 Java 로직과 동일: plm_admin 사용자만 관리자로 인식
  if (req.user.userId === "plm_admin") {
    next();
  } else {
    res.status(403).json({
      success: false,
      error: {
        code: "ADMIN_REQUIRED",
        details: "관리자 권한이 필요합니다.",
      },
    });
  }
};

/**
 * 특정 사용자 또는 관리자 권한 확인 미들웨어
 */
export const requireUserOrAdmin = (targetUserId: string) => {
  return (
    req: AuthenticatedRequest,
    res: Response,
    next: NextFunction
  ): void => {
    if (!req.user) {
      res.status(401).json({
        success: false,
        error: {
          code: "AUTHENTICATION_REQUIRED",
          details: "인증이 필요합니다.",
        },
      });
      return;
    }

    // 본인 또는 관리자인 경우 통과
    if (req.user.userId === targetUserId || req.user.userId === "plm_admin") {
      next();
    } else {
      res.status(403).json({
        success: false,
        error: {
          code: "PERMISSION_DENIED",
          details: "권한이 없습니다.",
        },
      });
    }
  };
};

/**
 * 토큰 갱신 미들웨어
 * 토큰이 곧 만료될 경우 자동으로 갱신
 */
export const refreshTokenIfNeeded = (
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): void => {
  try {
    const authHeader = req.get("Authorization");
    const token = authHeader && authHeader.split(" ")[1];

    if (token) {
      // 토큰이 1시간 이내에 만료되는지 확인
      const decoded = JwtUtils.decodeToken(token);
      if (decoded && decoded.exp) {
        const currentTime = Math.floor(Date.now() / 1000);
        const timeUntilExpiry = decoded.exp - currentTime;

        // 1시간(3600초) 이내에 만료되는 경우 갱신
        if (timeUntilExpiry > 0 && timeUntilExpiry < 3600) {
          const newToken = JwtUtils.refreshToken(token);

          // 새로운 토큰을 응답 헤더에 포함
          res.setHeader("X-New-Token", newToken);
          logger.info(`토큰 갱신: ${decoded.userId} (${req.ip})`);
        }
      }
    }

    next();
  } catch (error) {
    // 토큰 갱신 실패해도 요청은 계속 진행
    logger.warn(
      `토큰 갱신 실패: ${error instanceof Error ? error.message : "Unknown error"}`
    );
    next();
  }
};

/**
 * 인증 상태 확인 미들웨어
 * 토큰 유효성만 확인하고 사용자 정보는 설정하지 않음
 */
export const checkAuthStatus = (
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  try {
    const authHeader = req.get("Authorization");
    const token = authHeader && authHeader.split(" ")[1];

    if (!token) {
      res.status(200).json({
        success: true,
        data: {
          isAuthenticated: false,
        },
      });
      return;
    }

    const validation = JwtUtils.validateToken(token);

    res.status(200).json({
      success: true,
      data: {
        isAuthenticated: validation.isValid,
        error: validation.error,
      },
    });
  } catch (error) {
    logger.error(
      `인증 상태 확인 실패: ${error instanceof Error ? error.message : "Unknown error"}`
    );

    res.status(200).json({
      success: true,
      data: {
        isAuthenticated: false,
        error: "인증 상태 확인 중 오류가 발생했습니다.",
      },
    });
  }
};
