// Authentication middleware for API routes
import { NextApiRequest, NextApiResponse } from 'next';
import jwt from 'jsonwebtoken';
import { userService } from '@/lib/database/mysql-service';

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';

export interface AuthenticatedRequest extends NextApiRequest {
  user: {
    id: number;
    email: string;
    role: string;
    shopId: number;
  };
  shopId: number;
}

export function withAuth(handler: (req: AuthenticatedRequest, res: NextApiResponse) => Promise<void>) {
  return async (req: NextApiRequest, res: NextApiResponse) => {
    try {
      // Get token from Authorization header
      const authHeader = req.headers.authorization;
      if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'No token provided' });
      }

      const token = authHeader.substring(7); // Remove 'Bearer ' prefix

      // Verify token
      const decoded = jwt.verify(token, JWT_SECRET) as any;
      
      // Get user from database
      const user = await userService.findById(decoded.userId);
      if (!user || !user.is_active) {
        return res.status(401).json({ error: 'Invalid or inactive user' });
      }

      // Add user info to request
      (req as AuthenticatedRequest).user = {
        id: user.id,
        email: user.email,
        role: user.role,
        shopId: user.shop_id,
      };
      (req as AuthenticatedRequest).shopId = user.shop_id;

      return handler(req as AuthenticatedRequest, res);
    } catch (error: any) {
      console.error('Auth middleware error:', error);
      
      if (error.name === 'JsonWebTokenError') {
        return res.status(401).json({ error: 'Invalid token' });
      }
      
      if (error.name === 'TokenExpiredError') {
        return res.status(401).json({ error: 'Token expired' });
      }
      
      return res.status(500).json({ error: 'Authentication error' });
    }
  };
}

// Optional auth - doesn't fail if no token provided
export function withOptionalAuth(handler: (req: NextApiRequest, res: NextApiResponse) => Promise<void>) {
  return async (req: NextApiRequest, res: NextApiResponse) => {
    try {
      const authHeader = req.headers.authorization;
      
      if (authHeader && authHeader.startsWith('Bearer ')) {
        const token = authHeader.substring(7);
        const decoded = jwt.verify(token, JWT_SECRET) as any;
        const user = await userService.findById(decoded.userId);
        
        if (user && user.is_active) {
          (req as AuthenticatedRequest).user = {
            id: user.id,
            email: user.email,
            role: user.role,
            shopId: user.shop_id,
          };
          (req as AuthenticatedRequest).shopId = user.shop_id;
        }
      }

      return handler(req, res);
    } catch (error) {
      // Ignore auth errors for optional auth
      return handler(req, res);
    }
  };
}

// Role-based authorization
export function withRole(roles: string[]) {
  return function(handler: (req: AuthenticatedRequest, res: NextApiResponse) => Promise<void>) {
    return withAuth(async (req: AuthenticatedRequest, res: NextApiResponse) => {
      if (!roles.includes(req.user.role)) {
        return res.status(403).json({ error: 'Insufficient permissions' });
      }
      
      return handler(req, res);
    });
  };
}

// Generate JWT token
export function generateToken(userId: number): string {
  return jwt.sign(
    { userId },
    JWT_SECRET,
    { expiresIn: '7d' }
  );
}

// Verify password (you'll need to implement password hashing)
export async function verifyPassword(plainPassword: string, hashedPassword: string): Promise<boolean> {
  // This is a simple comparison - in production, use bcrypt or similar
  const bcrypt = require('bcrypt');
  return bcrypt.compare(plainPassword, hashedPassword);
}

// Hash password
export async function hashPassword(password: string): Promise<string> {
  const bcrypt = require('bcrypt');
  return bcrypt.hash(password, 10);
}
