/**
 * Error Formatter Utility
 * Format error dengan detail lengkap untuk memudahkan debugging
 */

const path = require('path');
const config = require('../config.json');

class ErrorFormatter {
	/**
	 * Format error menjadi response yang informatif
	 * @param {Error} error - Error object
	 * @param {Object} req - Express request object
	 * @param {boolean} includeStack - Include stack trace (default: true di development)
	 * @returns {Object} Formatted error response
	 */
	static format(error, req = null, includeStack = null) {
		const isDevelopment = config.backendConfig?.NODE_ENV === 'development';
		const shouldIncludeStack = includeStack !== null ? includeStack : isDevelopment;

		// Base error info
		const errorInfo = {
			success: false,
			error: {
				name: error.name || 'Error',
				message: error.message || 'An error occurred',
				type: this.detectErrorType(error),
				timestamp: new Date().toISOString(),
			},
		};

		// Add request context if available
		if (req) {
			errorInfo.request = this.getRequestContext(req);
		}

		// Add error-specific details
		const errorDetails = this.getErrorDetails(error);
		if (errorDetails) {
			errorInfo.error.details = errorDetails;
		}

		// Add stack trace in development
		if (shouldIncludeStack && error.stack) {
			errorInfo.error.stack = this.formatStack(error.stack);
			errorInfo.error.location = this.extractLocation(error.stack);
		}

		// Add error code if available
		if (error.code) {
			errorInfo.error.code = error.code;
		}

		// Add SQL error details if available
		if (error.sql) {
			errorInfo.error.sql = {
				query: error.sql,
				parameters: error.parameters || null,
			};
		}

		return errorInfo;
	}

	/**
	 * Detect error type
	 */
	static detectErrorType(error) {
		// Sequelize errors
		if (error.name === 'SequelizeValidationError') return 'VALIDATION_ERROR';
		if (error.name === 'SequelizeUniqueConstraintError') return 'UNIQUE_CONSTRAINT_ERROR';
		if (error.name === 'SequelizeForeignKeyConstraintError') return 'FOREIGN_KEY_ERROR';
		if (error.name === 'SequelizeDatabaseError') return 'DATABASE_ERROR';
		if (error.name === 'SequelizeConnectionError') return 'CONNECTION_ERROR';
		if (error.name === 'SequelizeTimeoutError') return 'TIMEOUT_ERROR';

		// Joi validation errors
		if (error.isJoi) return 'VALIDATION_ERROR';

		// HTTP errors
		if (error.statusCode) return 'HTTP_ERROR';
		
		// Payload too large error
		if (error.name === 'PayloadTooLargeError' || error.type === 'entity.too.large') {
			return 'PAYLOAD_TOO_LARGE_ERROR';
		}

		// Authentication errors
		if (error.name === 'UnauthorizedError' || error.name === 'JsonWebTokenError') return 'AUTHENTICATION_ERROR';

		// Custom validation errors
		if (error.name === 'ValidationError' || error.validation) return 'VALIDATION_ERROR';

		// Default
		return 'UNKNOWN_ERROR';
	}

	/**
	 * Get error-specific details
	 */
	static getErrorDetails(error) {
		// Sequelize ValidationError
		if (error.name === 'SequelizeValidationError' && error.errors) {
			const details = {};
			error.errors.forEach((err) => {
				details[err.path] = {
					message: err.message,
					type: err.type,
					value: err.value,
					validator: err.validatorKey,
				};
			});
			return {
				type: 'field_validation',
				fields: details,
				summary: `${error.errors.length} validation error(s)`,
			};
		}

		// Sequelize UniqueConstraintError
		if (error.name === 'SequelizeUniqueConstraintError' && error.errors) {
			const details = {};
			error.errors.forEach((err) => {
				details[err.path] = {
					message: err.message,
					value: err.value,
					constraint: err.path,
				};
			});
			return {
				type: 'unique_constraint',
				fields: details,
				message: 'Duplicate entry detected. The value must be unique.',
			};
		}

		// Sequelize ForeignKeyConstraintError
		if (error.name === 'SequelizeForeignKeyConstraintError') {
			return {
				type: 'foreign_key',
				table: error.table,
				constraint: error.index,
				message: `Foreign key constraint failed. Referenced record does not exist in ${error.table}.`,
			};
		}

		// Joi validation errors
		if (error.isJoi && error.details) {
			const details = {};
			error.details.forEach((detail) => {
				const path = detail.path.join('.');
				details[path] = {
					message: detail.message,
					type: detail.type,
					value: detail.context?.value,
					limit: detail.context?.limit,
				};
			});
			return {
				type: 'joi_validation',
				fields: details,
				summary: `${error.details.length} validation error(s)`,
			};
		}

		// Custom validation errors
		if (error.validation) {
			return {
				type: 'custom_validation',
				fields: error.validation,
			};
		}

		// Database error details
		if (error.name === 'SequelizeDatabaseError') {
			return {
				type: 'database',
				original: error.original?.message || error.message,
				sqlState: error.original?.sqlState,
				sqlMessage: error.original?.sqlMessage,
			};
		}

		return null;
	}

	/**
	 * Get request context
	 */
	static getRequestContext(req) {
		const context = {
			method: req.method,
			url: req.originalUrl || req.url,
			path: req.path,
			ip: req.ip || req.connection?.remoteAddress,
			userAgent: req.get('user-agent'),
			timestamp: new Date().toISOString(),
		};

		// Add query params (sanitized)
		if (Object.keys(req.query || {}).length > 0) {
			context.query = this.sanitizeObject(req.query);
		}

		// Add body params (sanitized, exclude sensitive data)
		if (req.body && Object.keys(req.body).length > 0) {
			context.body = this.sanitizeObject(req.body);
		}

		// Add params
		if (req.params && Object.keys(req.params).length > 0) {
			context.params = req.params;
		}

		// Add request ID if available
		if (req.id || req.headers['x-request-id']) {
			context.requestId = req.id || req.headers['x-request-id'];
		}

		return context;
	}

	/**
	 * Sanitize object (remove sensitive data)
	 */
	static sanitizeObject(obj) {
		const sensitiveKeys = ['password', 'hash', 'token', 'secret', 'apiKey', 'api_key', 'authorization'];
		const sanitized = { ...obj };

		for (const key of sensitiveKeys) {
			if (sanitized[key]) {
				sanitized[key] = '***REDACTED***';
			}
		}

		return sanitized;
	}

	/**
	 * Format stack trace untuk readability
	 */
	static formatStack(stack) {
		if (!stack) return null;

		const lines = stack.split('\n');
		const formatted = [];

		lines.forEach((line, index) => {
			if (index === 0) {
				formatted.push(line.trim());
			} else if (line.trim().startsWith('at ')) {
				// Format stack trace line
				const cleaned = line
					.trim()
					.replace(/^at /, '')
					.replace(/\(/g, ' → ')
					.replace(/\)/g, '');
				formatted.push(`  ${cleaned}`);
			}
		});

		return formatted.join('\n');
	}

	/**
	 * Extract file location from stack trace
	 */
	static extractLocation(stack) {
		if (!stack) return null;

		const lines = stack.split('\n');
		// Find first line with file path (usually second line)
		for (let i = 1; i < lines.length; i++) {
			const line = lines[i];
			const match = line.match(/\((.+):(\d+):(\d+)\)/);
			if (match) {
				const [, filePath, lineNum, colNum] = match;
				// Get relative path from project root
				const relativePath = path.relative(process.cwd(), filePath);
				return {
					file: relativePath,
					absolute: filePath,
					line: parseInt(lineNum, 10),
					column: parseInt(colNum, 10),
				};
			}
		}

		return null;
	}

	/**
	 * Get HTTP status code based on error type
	 */
	static getStatusCode(error) {
		// Payload too large error
		if (error.name === 'PayloadTooLargeError' || error.type === 'entity.too.large') {
			return 413; // Payload Too Large
		}

		// Validation errors
		if (
			error.name === 'SequelizeValidationError' ||
			error.name === 'ValidationError' ||
			error.isJoi ||
			error.validation
		) {
			return 400;
		}

		// Unique constraint errors
		if (error.name === 'SequelizeUniqueConstraintError') {
			return 409; // Conflict
		}

		// Foreign key errors
		if (error.name === 'SequelizeForeignKeyConstraintError') {
			return 400;
		}

		// Authentication errors
		if (error.name === 'UnauthorizedError' || 
		    error.name === 'JsonWebTokenError' || 
		    error.name === 'TokenExpiredError' ||
		    error.code === 'TOKEN_EXPIRED' ||
		    error.code === 'INVALID_TOKEN' ||
		    error.code === 'UNAUTHORIZED' ||
		    error.code === 'NO_TOKEN') {
			return 401;
		}

		// Not found errors
		if (typeof error === 'string' && error.toLowerCase().endsWith('not found')) {
			return 404;
		}
		if (error.statusCode === 404) {
			return 404;
		}

		// Database connection errors
		if (error.name === 'SequelizeConnectionError' || error.name === 'SequelizeTimeoutError') {
			return 503; // Service Unavailable
		}

		// Database errors
		if (error.name === 'SequelizeDatabaseError') {
			return 500;
		}

		// Custom status code
		if (error.statusCode) {
			return error.statusCode;
		}

		// Default
		return 500;
	}

	/**
	 * Create user-friendly error message
	 */
	static getUserMessage(error) {
		// Payload too large error
		if (error.name === 'PayloadTooLargeError' || error.type === 'entity.too.large') {
			return 'Request payload is too large. Please reduce the number of items or split into smaller batches. Maximum size: 50MB.';
		}

		// Sequelize ValidationError
		if (error.name === 'SequelizeValidationError' && error.errors) {
			const firstError = error.errors[0];
			return `Validation failed: ${firstError.message}`;
		}

		// Sequelize UniqueConstraintError
		if (error.name === 'SequelizeUniqueConstraintError') {
			if (error.errors && error.errors.length > 0) {
				const firstError = error.errors[0];
				const fieldName = firstError.path || 'field';
				const fieldValue = firstError.value || '';
				return `${fieldName} "${fieldValue}" already exists. Please use a different value.`;
			}
			return 'This record already exists. Please use a different value.';
		}

		// Sequelize ForeignKeyConstraintError
		if (error.name === 'SequelizeForeignKeyConstraintError') {
			return 'Cannot perform this operation. Referenced record does not exist.';
		}

		// Joi validation
		if (error.isJoi && error.details) {
			const firstError = error.details[0];
			return `Validation failed: ${firstError.message}`;
		}

		// Custom validation
		if (error.validation) {
			return 'Validation failed. Please check your input.';
		}

		// Database connection
		if (error.name === 'SequelizeConnectionError') {
			return 'Database connection failed. Please try again later.';
		}

		// ROBUST: Handle JWT token errors
		if (error.name === 'TokenExpiredError' || error.code === 'TOKEN_EXPIRED') {
			return 'Token expired. Please login again.';
		}
		
		if (error.name === 'JsonWebTokenError' || error.code === 'INVALID_TOKEN') {
			return 'Invalid token. Please login again.';
		}
		
		if (error.name === 'UnauthorizedError' || error.code === 'UNAUTHORIZED' || error.code === 'NO_TOKEN') {
			return 'Authentication required. Please login again.';
		}

		// Default
		return error.message || 'An error occurred. Please try again.';
	}
}

module.exports = ErrorFormatter;
