import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { JWT_CONFIG } from '../config/jwt';
import { User } from '../models/User';
import { resolveRoleByUserField } from '../utils/resolveRoleByUserField';
import { isCsmEnabled } from '../csm/config';

export interface AuthRequest extends Request {
  user?: {
    _id: string;
    email: string;
    role: string;
    name: string;
    /** true when the identity comes from CSM delegation (no local users row). */
    csm?: boolean;
  };
}

export const authenticate = async (
  req: AuthRequest,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    const authHeader = req.headers.authorization;
    const startTime = Date.now();

    // Debug logging
    // console.log('[Auth Middleware] Request received:', {
    //   method: req.method,
    //   path: req.path,
    //   hasAuthHeader: !!authHeader,
    //   authHeaderPreview: authHeader ? `${authHeader.substring(0, 20)}...` : 'none',
    //   allHeaders: Object.keys(req.headers),
    // });

    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      console.log('[Auth Middleware] No valid Authorization header');
      res.status(401).json({
        success: false,
        message: 'No token provided. Authorization header required.',
      });
      return;
    }

    const token = authHeader.substring(7); // Remove 'Bearer ' prefix

    // console.log('[Auth Middleware] Token extracted:', {
    //   tokenLength: token.length,
    //   tokenPreview: `${token.substring(0, 20)}...`,
    //   jwtSecretLength: JWT_CONFIG.secret.length,
    // });

    try {
      const decoded = jwt.verify(token, JWT_CONFIG.secret) as {
        userId: string;
        email: string;
        role?: string;
        name?: string;
        csm?: boolean;
      };

      // CSM delegation: trust the (signature-verified) JWT claims directly.
      // The user has no local `users` row, so a DB lookup would wrongly 401 them.
      // RBAC still works: req.user.role is an AOM Role code and requirePermission
      // resolves it against the local Role collection.
      if (isCsmEnabled() && decoded.csm) {
        req.user = {
          _id: String(decoded.userId),
          email: decoded.email,
          role: decoded.role || 'TECHNICIAN',
          name: decoded.name || decoded.email,
          csm: true,
        };
        next();
        return;
      }

      // Get user from database
      // Use email to find user (more reliable than _id which can be ObjectId or string)
      // This handles both demo data (string _id) and production data (ObjectId _id)
      let user = await User.findOne({ email: decoded.email.toLowerCase().trim() })
        .select('-password')
        .lean();

      // If not found by email, try by _id (for backward compatibility)
      if (!user && decoded.userId) {
        const mongoose = await import('mongoose');

        // Check if userId is a valid MongoDB ObjectId
        if (mongoose.Types.ObjectId.isValid(decoded.userId)) {
          try {
            user = await User.findById(decoded.userId)
              .select('-password')
              .lean();
          } catch (findError: any) {
            // If findById fails (e.g., CastError), try using native MongoDB collection
            console.log('[Auth Middleware] findById failed, trying native MongoDB query:', findError.message);
            try {
              const db = mongoose.connection.db;
              if (!db) throw new Error('Database connection not established');
              const usersCollection = db.collection('users');
              user = await usersCollection.findOne(
                { _id: decoded.userId } as any,
                { projection: { password: 0 } }
              ) as any;
            } catch (nativeError) {
              console.error('[Auth Middleware] Native MongoDB query also failed:', nativeError);
            }
          }
        } else {
          // userId is not a valid ObjectId (likely a string ID from demo data)
          // Use native MongoDB collection to bypass Mongoose casting
          try {
            const db = mongoose.connection.db;
            if (!db) throw new Error('Database connection not established');
            const usersCollection = db.collection('users');
            user = await usersCollection.findOne(
              { _id: decoded.userId } as any,
              { projection: { password: 0 } }
            ) as any;
          } catch (nativeError) {
            console.error('[Auth Middleware] Native MongoDB query failed:', nativeError);
          }
        }
      }

      if (!user || user.status !== 'Active') {
        console.log('[Auth Middleware] User not found or inactive:', {
          userId: decoded.userId,
          email: decoded.email,
          userFound: !!user,
          userStatus: user?.status,
        });
        res.status(401).json({
          success: false,
          message: 'User not found or inactive',
        });
        return;
      }

      // console.log('[Auth Middleware] User authenticated:', {
      //   userId: user._id,
      //   email: user.email,
      //   role: user.role,
      // });

      // Attach user to request
      req.user = {
        _id: user._id.toString(),
        email: user.email,
        role: user.role,
        name: user.name,
      };

      const duration = Date.now() - startTime;
      if (duration > 1000) {
        console.warn(`[Auth Middleware] Slow authentication: ${duration}ms for user ${user?.email}`);
      }
      next();
    } catch (error: any) {
      console.error('[Auth Middleware] Token verification failed:', {
        errorName: error.name,
        errorMessage: error.message,
        tokenPreview: `${token.substring(0, 20)}...`,
      });

      if (error.name === 'TokenExpiredError') {
        res.status(401).json({
          success: false,
          message: 'Token expired',
        });
        return;
      }

      res.status(401).json({
        success: false,
        message: 'Invalid token',
      });
      return;
    }
  } catch (error: any) {
    console.error('[Auth Middleware] Unexpected error:', error);
    res.status(500).json({
      success: false,
      message: 'Authentication error',
    });
  }
};

export const authorize = (...roles: string[]) => {
  return (req: AuthRequest, res: Response, next: NextFunction): void => {
    if (!req.user) {
      res.status(401).json({
        success: false,
        message: 'Authentication required',
      });
      return;
    }

    if (!roles.includes(req.user.role)) {
      res.status(403).json({
        success: false,
        message: 'Insufficient permissions',
      });
      return;
    }

    next();
  };
};

/** RBAC: kiểm tra `Role.permissions[permissionKey]` (ADMIN bỏ qua). */
export const requirePermission = (permissionKey: string) => {
  return async (req: AuthRequest, res: Response, next: NextFunction): Promise<void> => {
    if (!req.user) {
      res.status(401).json({ success: false, message: 'Authentication required' });
      return;
    }
    try {
      const role = await resolveRoleByUserField(req.user.role);
      if (!role) {
        res.status(403).json({ success: false, message: 'Forbidden' });
        return;
      }
      if (role.code === 'ADMIN') {
        next();
        return;
      }
      if (role.permissions?.[permissionKey] === true) {
        next();
        return;
      }
      res.status(403).json({ success: false, message: 'Insufficient permissions' });
    } catch (e) {
      console.error('[requirePermission]', e);
      res.status(500).json({ success: false, message: 'Permission check failed' });
    }
  };
};

/** Đổi mật khẩu: chính user đó hoặc có `manage_users`. */
export const allowSelfOrManageUsers = async (
  req: AuthRequest,
  res: Response,
  next: NextFunction
): Promise<void> => {
  if (!req.user) {
    res.status(401).json({ success: false, message: 'Authentication required' });
    return;
  }
  const targetId = String(req.params.id ?? '');
  if (targetId && req.user._id === targetId) {
    next();
    return;
  }
  await requirePermission('manage_users')(req, res, next);
};

/** RBAC: cần ít nhất một trong các quyền (ADMIN bỏ qua). */
export const requireAnyPermission = (...permissionKeys: string[]) => {
  return async (req: AuthRequest, res: Response, next: NextFunction): Promise<void> => {
    if (!req.user) {
      res.status(401).json({ success: false, message: 'Authentication required' });
      return;
    }
    if (permissionKeys.length === 0) {
      next();
      return;
    }
    try {
      const role = await resolveRoleByUserField(req.user.role);
      if (!role) {
        res.status(403).json({ success: false, message: 'Forbidden' });
        return;
      }
      if (role.code === 'ADMIN') {
        next();
        return;
      }
      const ok = permissionKeys.some((k) => role.permissions?.[k] === true);
      if (ok) {
        next();
        return;
      }
      res.status(403).json({ success: false, message: 'Insufficient permissions' });
    } catch (e) {
      console.error('[requireAnyPermission]', e);
      res.status(500).json({ success: false, message: 'Permission check failed' });
    }
  };
};
