/**
 * Express Integration Example
 *
 * This file demonstrates how to integrate the error handling system
 * into an Express application.
 *
 * @example Integration Steps:
 * 1. Set up error middleware (must be last)
 * 2. Optionally add 404 handler
 * 3. Use custom error classes in your routes
 * 4. Configure request ID generation (optional)
 */

import express, { Request, Response, NextFunction, Router } from 'express';
import {
  createErrorMiddleware,
  notFoundMiddleware,
  NotFoundError,
  ValidationError,
  UnauthorizedError,
  BadRequestError,
  BaseError,
} from '@erp-suite/core';

// ========================================
// Basic Express App Setup
// ========================================

/**
 * Create Express app with error handling
 */
function createApp() {
  const app = express();

  // Body parsing middleware
  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));

  // Request ID middleware (optional but recommended)
  app.use(requestIdMiddleware);

  // Your routes
  app.use('/api/users', usersRouter);
  app.use('/api/products', productsRouter);

  // 404 handler (must be after all routes)
  app.use(notFoundMiddleware);

  // Error handling middleware (must be last)
  app.use(createErrorMiddleware({
    includeStackTrace: process.env.NODE_ENV !== 'production',
  }));

  return app;
}

// ========================================
// Request ID Middleware (Optional)
// ========================================

import { randomUUID } from 'crypto';

/**
 * Middleware to generate and track request IDs
 */
function requestIdMiddleware(req: Request, res: Response, next: NextFunction) {
  const requestId =
    (req.headers['x-request-id'] as string) ||
    (req.headers['x-correlation-id'] as string) ||
    randomUUID();

  // Attach to request for access in handlers
  (req as any).requestId = requestId;

  // Include in response headers
  res.setHeader('X-Request-ID', requestId);

  next();
}

// ========================================
// Custom Logger Integration
// ========================================

import { ErrorLogger } from '@erp-suite/core';

/**
 * Custom logger implementation (e.g., Winston, Pino)
 */
class WinstonLogger implements ErrorLogger {
  error(message: string, ...meta: any[]): void {
    // winston.error(message, ...meta);
    console.error('[ERROR]', message, ...meta);
  }

  warn(message: string, ...meta: any[]): void {
    // winston.warn(message, ...meta);
    console.warn('[WARN]', message, ...meta);
  }

  log(message: string, ...meta: any[]): void {
    // winston.info(message, ...meta);
    console.log('[INFO]', message, ...meta);
  }
}

/**
 * App with custom logger
 */
function createAppWithCustomLogger() {
  const app = express();

  app.use(express.json());
  app.use('/api/users', usersRouter);

  // Error middleware with custom logger
  app.use(createErrorMiddleware({
    logger: new WinstonLogger(),
    includeStackTrace: process.env.NODE_ENV !== 'production',
  }));

  return app;
}

// ========================================
// Users Router Example
// ========================================

interface User {
  id: string;
  email: string;
  name: string;
}

// Mock database
const users: User[] = [
  { id: '1', email: 'user1@example.com', name: 'User One' },
  { id: '2', email: 'user2@example.com', name: 'User Two' },
];

const usersRouter = Router();

/**
 * GET /api/users/:id
 *
 * Demonstrates NotFoundError handling
 */
usersRouter.get('/:id', (req: Request, res: Response, next: NextFunction) => {
  try {
    const user = users.find(u => u.id === req.params.id);

    if (!user) {
      throw new NotFoundError('User not found', { userId: req.params.id });
    }

    res.json(user);
  } catch (error) {
    next(error); // Pass to error middleware
  }
});

/**
 * POST /api/users
 *
 * Demonstrates ValidationError handling
 */
usersRouter.post('/', (req: Request, res: Response, next: NextFunction) => {
  try {
    const { email, name } = req.body;

    // Validation
    const errors: any[] = [];

    if (!email || !email.includes('@')) {
      errors.push({ field: 'email', message: 'Valid email is required' });
    }

    if (!name || name.length < 2) {
      errors.push({ field: 'name', message: 'Name must be at least 2 characters' });
    }

    if (errors.length > 0) {
      throw new ValidationError('Validation failed', { errors });
    }

    // Check for duplicate email
    const existing = users.find(u => u.email === email);
    if (existing) {
      throw new BadRequestError('Email already exists', {
        email,
        existingUserId: existing.id,
      });
    }

    // Create user
    const user: User = {
      id: String(users.length + 1),
      email,
      name,
    };

    users.push(user);

    res.status(201).json(user);
  } catch (error) {
    next(error);
  }
});

/**
 * Async/await route handler with error handling
 */
usersRouter.get(
  '/:id/profile',
  asyncHandler(async (req: Request, res: Response) => {
    const user = await findUserById(req.params.id);
    const profile = await getUserProfile(user.id);

    res.json(profile);
  })
);

// ========================================
// Async Handler Wrapper
// ========================================

/**
 * Wraps async route handlers to automatically catch errors
 *
 * Usage: app.get('/route', asyncHandler(async (req, res) => { ... }))
 */
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);
  };
}

// ========================================
// Products Router Example
// ========================================

const productsRouter = Router();

/**
 * Protected route example
 */
productsRouter.get(
  '/',
  authMiddleware, // Authentication middleware
  asyncHandler(async (req: Request, res: Response) => {
    // This route is protected and will throw UnauthorizedError
    // if authentication fails
    const products = await getProducts();
    res.json(products);
  })
);

/**
 * Authentication middleware example
 */
function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const token = req.headers.authorization?.replace('Bearer ', '');

  if (!token) {
    throw new UnauthorizedError('Authentication token required');
  }

  // Validate token...
  if (token !== 'valid-token') {
    throw new UnauthorizedError('Invalid or expired token', {
      providedToken: token.substring(0, 10) + '...',
    });
  }

  // Attach user to request
  (req as any).user = { id: '1', email: 'user@example.com' };
  next();
}

// ========================================
// Service Layer Example
// ========================================

/**
 * Service layer with error handling
 */
class UserService {
  async findById(id: string): Promise<User> {
    const user = users.find(u => u.id === id);

    if (!user) {
      throw new NotFoundError('User not found', { userId: id });
    }

    return user;
  }

  async create(email: string, name: string): Promise<User> {
    // Validation
    if (!email || !email.includes('@')) {
      throw new ValidationError('Invalid email address', {
        field: 'email',
        value: email,
      });
    }

    // Business logic validation
    const existing = users.find(u => u.email === email);
    if (existing) {
      throw new BadRequestError('Email already exists', {
        email,
        existingUserId: existing.id,
      });
    }

    const user: User = {
      id: String(users.length + 1),
      email,
      name,
    };

    users.push(user);
    return user;
  }
}

// ========================================
// Custom Domain Errors
// ========================================

/**
 * Create custom errors for your domain
 */
class InsufficientBalanceError extends BaseError {
  readonly statusCode = 400;
  readonly error = 'Insufficient Balance';

  constructor(required: number, available: number) {
    super('Insufficient balance for this operation', {
      required,
      available,
      deficit: required - available,
    });
  }
}

class PaymentService {
  async processPayment(userId: string, amount: number): Promise<void> {
    const balance = await this.getBalance(userId);

    if (balance < amount) {
      throw new InsufficientBalanceError(amount, balance);
    }

    // Process payment...
  }

  private async getBalance(userId: string): Promise<number> {
    return 100; // Mock
  }
}

// ========================================
// Error Response Examples
// ========================================

/**
 * Example error responses generated by the system:
 *
 * 404 Not Found:
 * GET /api/users/999
 * {
 *   "statusCode": 404,
 *   "error": "Not Found",
 *   "message": "User not found",
 *   "details": { "userId": "999" },
 *   "timestamp": "2025-12-12T10:30:00.000Z",
 *   "path": "/api/users/999",
 *   "requestId": "550e8400-e29b-41d4-a716-446655440000"
 * }
 *
 * 422 Validation Error:
 * POST /api/users { "email": "invalid", "name": "A" }
 * {
 *   "statusCode": 422,
 *   "error": "Validation Error",
 *   "message": "Validation failed",
 *   "details": {
 *     "errors": [
 *       { "field": "email", "message": "Valid email is required" },
 *       { "field": "name", "message": "Name must be at least 2 characters" }
 *     ]
 *   },
 *   "timestamp": "2025-12-12T10:30:00.000Z",
 *   "path": "/api/users",
 *   "requestId": "550e8400-e29b-41d4-a716-446655440001"
 * }
 *
 * 401 Unauthorized:
 * GET /api/products (without token)
 * {
 *   "statusCode": 401,
 *   "error": "Unauthorized",
 *   "message": "Authentication token required",
 *   "timestamp": "2025-12-12T10:30:00.000Z",
 *   "path": "/api/products",
 *   "requestId": "550e8400-e29b-41d4-a716-446655440002"
 * }
 *
 * 400 Bad Request:
 * POST /api/users { "email": "existing@example.com", "name": "Test" }
 * {
 *   "statusCode": 400,
 *   "error": "Bad Request",
 *   "message": "Email already exists",
 *   "details": {
 *     "email": "existing@example.com",
 *     "existingUserId": "1"
 *   },
 *   "timestamp": "2025-12-12T10:30:00.000Z",
 *   "path": "/api/users",
 *   "requestId": "550e8400-e29b-41d4-a716-446655440003"
 * }
 */

// ========================================
// Helper Functions (Mock)
// ========================================

async function findUserById(id: string): Promise<User> {
  const user = users.find(u => u.id === id);
  if (!user) {
    throw new NotFoundError('User not found', { userId: id });
  }
  return user;
}

async function getUserProfile(userId: string): Promise<any> {
  return { userId, bio: 'User bio', avatar: 'avatar.jpg' };
}

async function getProducts(): Promise<any[]> {
  return [
    { id: '1', name: 'Product 1', price: 100 },
    { id: '2', name: 'Product 2', price: 200 },
  ];
}

// ========================================
// Start Server
// ========================================

if (require.main === module) {
  const app = createApp();
  const PORT = process.env.PORT || 3000;

  app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
  });
}

export { createApp, createAppWithCustomLogger, asyncHandler };
