import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { User } from '../models/User';
import { ssoConfig } from '../config/sso';
import { ResponseHandler } from '../utils/responseHandler';
import { ApiTokenService } from '../services/apiToken.service';

const apiTokenService = new ApiTokenService();

interface JwtPayload {
  userId: string;
  employeeId: string;
  email: string;
  role: string;
  sessionToken: string;
  iat: number;
  exp: number;
}

export const authenticateToken = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // Try to get token from Authorization header first
    const authHeader = req.headers.authorization;
    let token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

    // Check if it's an API Token (starts with re_)
    if (token && token.startsWith('re_')) {
      const user = await apiTokenService.verifyToken(token);

      if (!user || !user.isActive) {
        ResponseHandler.unauthorized(res, 'Invalid or expired API token');
        return;
      }

      // Attach user info to request object
      req.user = {
        userId: user.userId,
        email: user.email,
        employeeId: user.employeeId || null,
        role: user.role
      };

      next();
      return;
    }

    // Fallback to cookie if available (requires cookie-parser middleware)

    // Fallback to cookie if available (requires cookie-parser middleware)
    if (!token && req.cookies?.accessToken) {
      token = req.cookies.accessToken;
    }

    if (!token) {
      ResponseHandler.unauthorized(res, 'Access token is required');
      return;
    }

    // Verify JWT token
    const decoded = jwt.verify(token, ssoConfig.jwtSecret) as JwtPayload;

    // Fetch user from database to ensure they still exist and are active
    const user = await User.findByPk(decoded.userId);

    if (!user || !user.isActive) {
      ResponseHandler.unauthorized(res, 'User not found or inactive');
      return;
    }

    if (!decoded.sessionToken || decoded.sessionToken !== user.sessionToken) {
      res.status(401).json({
        success: false,
        errorCode: 'SESSION_SUPERSEDED',
        message: `You have been logged out because an active session was detected from ${user.lastLoginDevice || 'another device'}.`
      });
      return;
    }

    // Attach user info to request object
    req.user = {
      userId: user.userId,
      email: user.email,
      employeeId: user.employeeId || null, // Optional - schema not finalized
      role: user.role // Keep uppercase: USER, MANAGEMENT, ADMIN
    };

    next();
  } catch (error: any) {
    if (error?.name === 'TokenExpiredError') {
      ResponseHandler.unauthorized(res, 'Token has expired');
    } else if (error?.name === 'JsonWebTokenError') {
      ResponseHandler.unauthorized(res, 'Invalid token');
    } else {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Authentication error', 500, errorMessage);
    }
  }
};

export const requireAdmin = (
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  if (req.user?.role !== 'ADMIN') {
    ResponseHandler.forbidden(res, 'Admin access required');
    return;
  }
  next();
};

export const optionalAuth = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    const authHeader = req.headers.authorization;
    const token = authHeader && authHeader.split(' ')[1];

    if (token) {
      const decoded = jwt.verify(token, ssoConfig.jwtSecret) as JwtPayload;
      const user = await User.findByPk(decoded.userId);

      if (user && user.isActive) {
        if (!decoded.sessionToken || decoded.sessionToken === user.sessionToken) {
          req.user = {
            userId: user.userId,
            email: user.email,
            employeeId: user.employeeId || null, // Optional - schema not finalized
            role: user.role // Keep uppercase: USER, MANAGEMENT, ADMIN
          };
        }
      }
    }

    next();
  } catch (error) {
    // For optional auth, we don't throw errors, just continue without user
    next();
  }
};
