/**
 * Error Handler Middleware
 */

import { Request, Response, NextFunction } from 'express';
import mongoose from 'mongoose';
import { MulterError } from 'multer';
import Anthropic from '@anthropic-ai/sdk';

interface CustomError extends Error {
  statusCode?: number;
  code?: number | string;
  errors?: any;
}

export const errorHandler = (
  err: CustomError,
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  console.error('Error:', err);

  // Mongoose validation error
  if (err instanceof mongoose.Error.ValidationError) {
    const messages = Object.values(err.errors).map((e: any) => e.message);
    res.status(400).json({
      error: 'Validation Error',
      messages
    });
    return;
  }

  // Mongoose duplicate key error
  if (err.code === 11000) {
    res.status(400).json({
      error: 'Duplicate Key Error',
      message: 'This record already exists'
    });
    return;
  }

  // Mongoose cast error (invalid ObjectId)
  if (err instanceof mongoose.Error.CastError) {
    res.status(400).json({
      error: 'Invalid ID Format',
      message: `Invalid ${err.path}: ${err.value}`
    });
    return;
  }

  // Multer file upload errors
  if (err instanceof MulterError) {
    if (err.code === 'LIMIT_FILE_SIZE') {
      res.status(413).json({
        error: 'Image size must be 5 MB or less',
      });
      return;
    }
    res.status(400).json({
      error: err.message || 'File upload error',
    });
    return;
  }

  // File type / format validation errors from multer fileFilter
  if (err.message && err.message.startsWith('Invalid file format')) {
    res.status(400).json({
      error: err.message,
    });
    return;
  }

  // JWT errors
  if (err.name === 'JsonWebTokenError') {
    res.status(401).json({
      error: 'Invalid Token',
      message: 'Please login again'
    });
    return;
  }

  if (err.name === 'TokenExpiredError') {
    res.status(401).json({
      error: 'Token Expired',
      message: 'Please login again'
    });
    return;
  }

  // Anthropic SDK errors — defense-in-depth for Claude API routes
  // Most SDK errors are caught in claudeService.ts and mapped to plain Errors,
  // but this handles any that bubble up to the global error handler.
  if (err instanceof Anthropic.AuthenticationError) {
    res.status(401).json({
      error: 'AI authentication failed. Check your API key.',
    });
    return;
  }
  if (err instanceof Anthropic.PermissionDeniedError) {
    res.status(403).json({
      error: 'AI permission denied. Your API key does not have access to this resource.',
    });
    return;
  }
  if (err instanceof Anthropic.BadRequestError) {
    res.status(400).json({
      error: `Bad request to AI: ${err.message}`,
    });
    return;
  }
  if (err instanceof Anthropic.NotFoundError) {
    res.status(404).json({
      error: 'AI model not found. Check the model ID.',
    });
    return;
  }
  if (err instanceof Anthropic.RateLimitError) {
    res.status(429).json({
      error: 'AI rate limit exceeded. Please try again later.',
    });
    return;
  }
  if (err instanceof Anthropic.InternalServerError) {
    res.status(502).json({
      error: 'AI service error. Please try again.',
    });
    return;
  }
  if (err instanceof Anthropic.APIError) {
    // Generic SDK error — use the HTTP status from the response
    // Handles overloaded (529) and other unclassified errors
    const status = err.status || 500;
    if (status === 529) {
      res.status(503).json({
        error: 'AI service temporarily overloaded. Please try again.',
      });
      return;
    }
    res.status(status).json({
      error: `AI error: ${err.message}`,
    });
    return;
  }

  // Default error
  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({
    success: false,
    error: 'Server Error',
    message: err.message || 'An unexpected error occurred',
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
  });
};

/**
 * Async handler wrapper for Express 4 route handlers.
 * Express 4 does NOT catch rejected promises in async handlers,
 * so unhandled errors silently disappear and the request hangs.
 * This wrapper ensures async errors are forwarded to the error handler.
 *
 * Usage: router.get('/path', asyncHandler(async (req, res) => { ... }))
 */
export function asyncHandler(fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) {
  return (req: Request, res: Response, next: NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}
