<?php
/**
 * Error Handler Utility
 *
 * Centralized error handling and logging for the API
 *
 * @package Care_API
 * @subpackage Utils
 * @version    1.0.0
 * @author     Descomplicar® <dev@descomplicar.pt>
 * @link       https://descomplicar.pt
 * @since      1.0.0
 */

namespace Care_API\Utils;

use WP_Error;
use WP_REST_Response;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class Error_Handler
 *
 * Comprehensive error handling and logging system
 *
 * @since 1.0.0
 */
class Error_Handler {

    /**
     * Error log file
     *
     * @var string
     */
    private static $log_file = null;

    /**
     * Initialize error handler
     *
     * @since 1.0.0
     */
    public static function init() {
        self::$log_file = WP_CONTENT_DIR . '/uploads/care-api-errors.log';
        
        // Ensure log directory exists
        $log_dir = dirname( self::$log_file );
        if ( ! file_exists( $log_dir ) ) {
            wp_mkdir_p( $log_dir );
        }
    }

    /**
     * Handle service errors from business logic layer
     *
     * @param WP_Error $error Service error
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_service_error( WP_Error $error ) {
        $error_code = $error->get_error_code();
        $error_message = $error->get_error_message();
        $error_data = $error->get_error_data();

        // Log the error
        self::log_error( $error_code, $error_message, $error_data );

        // Determine HTTP status code
        $status_code = 500; // Default server error
        if ( isset( $error_data['status'] ) ) {
            $status_code = $error_data['status'];
        } else {
            // Map error codes to HTTP status codes
            $status_map = array(
                'insufficient_permissions' => 403,
                'access_denied' => 403,
                'unauthorized' => 401,
                'not_found' => 404,
                'clinic_not_found' => 404,
                'patient_not_found' => 404,
                'doctor_not_found' => 404,
                'appointment_not_found' => 404,
                'encounter_not_found' => 404,
                'prescription_not_found' => 404,
                'bill_not_found' => 404,
                'validation_failed' => 400,
                'invalid_input' => 400,
                'business_rule_violation' => 400,
                'duplicate_entry' => 409,
                'conflict' => 409
            );

            if ( isset( $status_map[$error_code] ) ) {
                $status_code = $status_map[$error_code];
            }
        }

        // Prepare response data
        $response_data = array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message
            )
        );

        // Include additional error data if present
        if ( isset( $error_data['errors'] ) ) {
            $response_data['error']['details'] = $error_data['errors'];
        }

        if ( isset( $error_data['field'] ) ) {
            $response_data['error']['field'] = $error_data['field'];
        }

        return new WP_REST_Response( $response_data, $status_code );
    }

    /**
     * Handle exceptions from the application
     *
     * @param Exception $exception Exception object
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_exception( $exception ) {
        $error_code = 'server_error';
        $error_message = 'An internal server error occurred';
        
        // Log detailed exception information
        $error_details = array(
            'message' => $exception->getMessage(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTraceAsString()
        );

        self::log_error( $error_code, $error_message, $error_details );

        // In development mode, show detailed error
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            $error_message = $exception->getMessage();
            $response_data = array(
                'success' => false,
                'error' => array(
                    'code' => $error_code,
                    'message' => $error_message,
                    'file' => $exception->getFile(),
                    'line' => $exception->getLine()
                )
            );
        } else {
            // In production, show generic error
            $response_data = array(
                'success' => false,
                'error' => array(
                    'code' => $error_code,
                    'message' => $error_message
                )
            );
        }

        return new WP_REST_Response( $response_data, 500 );
    }

    /**
     * Handle authentication errors
     *
     * @param string $error_code Error code
     * @param string $error_message Error message
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_auth_error( $error_code = 'unauthorized', $error_message = 'Authentication required' ) {
        self::log_error( $error_code, $error_message );

        return new WP_REST_Response( array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message
            )
        ), 401 );
    }

    /**
     * Handle validation errors
     *
     * @param array  $errors Array of validation errors
     * @param string $field  Field that caused the error (optional)
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_validation_error( $errors, $field = null ) {
        $error_code = 'validation_failed';
        $error_message = 'Validation failed';

        self::log_error( $error_code, $error_message, array( 'errors' => $errors, 'field' => $field ) );

        $response_data = array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message,
                'details' => $errors
            )
        );

        if ( $field ) {
            $response_data['error']['field'] = $field;
        }

        return new WP_REST_Response( $response_data, 400 );
    }

    /**
     * Handle rate limiting errors
     *
     * @param int $retry_after Seconds until retry is allowed
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_rate_limit_error( $retry_after = 60 ) {
        $error_code = 'rate_limit_exceeded';
        $error_message = 'Too many requests. Please try again later.';

        self::log_error( $error_code, $error_message, array( 'retry_after' => $retry_after ) );

        $response = new WP_REST_Response( array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message,
                'retry_after' => $retry_after
            )
        ), 429 );

        $response->header( 'Retry-After', $retry_after );

        return $response;
    }

    /**
     * Handle database errors
     *
     * @param string $operation Database operation that failed
     * @param string $details   Error details
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_database_error( $operation, $details = '' ) {
        $error_code = 'database_error';
        $error_message = 'Database operation failed';

        self::log_error( $error_code, $error_message, array( 
            'operation' => $operation,
            'details' => $details 
        ) );

        return new WP_REST_Response( array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message
            )
        ), 500 );
    }

    /**
     * Handle permission errors
     *
     * @param string $action Action that was attempted
     * @param string $resource Resource that was accessed
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_permission_error( $action = '', $resource = '' ) {
        $error_code = 'insufficient_permissions';
        $error_message = 'You do not have permission to perform this action';

        if ( $action && $resource ) {
            $error_message = "You do not have permission to {$action} {$resource}";
        }

        self::log_error( $error_code, $error_message, array( 
            'action' => $action,
            'resource' => $resource,
            'user_id' => get_current_user_id()
        ) );

        return new WP_REST_Response( array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message
            )
        ), 403 );
    }

    /**
     * Handle not found errors
     *
     * @param string $resource Resource type
     * @param int    $id       Resource ID
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function handle_not_found_error( $resource = 'Resource', $id = null ) {
        $error_code = 'not_found';
        $error_message = "{$resource} not found";
        
        if ( $id ) {
            $error_message = "{$resource} with ID {$id} not found";
        }

        self::log_error( $error_code, $error_message, array( 
            'resource' => $resource,
            'id' => $id 
        ) );

        return new WP_REST_Response( array(
            'success' => false,
            'error' => array(
                'code' => $error_code,
                'message' => $error_message
            )
        ), 404 );
    }

    /**
     * Log error to file and WordPress error log
     *
     * @param string $error_code    Error code
     * @param string $error_message Error message
     * @param array  $error_data    Additional error data
     * @since 1.0.0
     */
    public static function log_error( $error_code, $error_message, $error_data = array() ) {
        $timestamp = current_time( 'Y-m-d H:i:s' );
        $user_id = get_current_user_id();
        $request_uri = $_SERVER['REQUEST_URI'] ?? '';
        $request_method = $_SERVER['REQUEST_METHOD'] ?? '';
        $user_ip = self::get_client_ip();

        // Prepare log entry
        $log_entry = array(
            'timestamp' => $timestamp,
            'error_code' => $error_code,
            'error_message' => $error_message,
            'user_id' => $user_id,
            'request_uri' => $request_uri,
            'request_method' => $request_method,
            'user_ip' => $user_ip,
            'error_data' => $error_data
        );

        // Log to file
        if ( self::$log_file ) {
            $log_line = json_encode( $log_entry ) . "\n";
            file_put_contents( self::$log_file, $log_line, FILE_APPEND | LOCK_EX );
        }

        // Log to WordPress error log
        $wp_log_message = sprintf(
            '[Care API] %s - %s (Code: %s, User: %d, IP: %s)',
            $error_message,
            $request_uri,
            $error_code,
            $user_id,
            $user_ip
        );

        if ( ! empty( $error_data ) ) {
            $wp_log_message .= ' - Data: ' . json_encode( $error_data );
        }

        error_log( $wp_log_message );

        // Store critical errors for admin notifications
        if ( self::is_critical_error( $error_code ) ) {
            self::store_critical_error( $log_entry );
        }
    }

    /**
     * Get client IP address
     *
     * @return string Client IP address
     * @since 1.0.0
     */
    private static function get_client_ip() {
        $ip_keys = array(
            'HTTP_CF_CONNECTING_IP',     // Cloudflare
            'HTTP_CLIENT_IP',            // Proxy
            'HTTP_X_FORWARDED_FOR',      // Load balancer/proxy
            'HTTP_X_FORWARDED',          // Proxy
            'HTTP_X_CLUSTER_CLIENT_IP',  // Cluster
            'HTTP_FORWARDED_FOR',        // Proxy
            'HTTP_FORWARDED',            // Proxy
            'REMOTE_ADDR'                // Standard
        );

        foreach ( $ip_keys as $key ) {
            if ( array_key_exists( $key, $_SERVER ) === true ) {
                $ip_list = explode( ',', $_SERVER[$key] );
                $ip = trim( $ip_list[0] );
                
                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
                    return $ip;
                }
            }
        }

        return $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    }

    /**
     * Check if error is critical
     *
     * @param string $error_code Error code
     * @return bool True if critical
     * @since 1.0.0
     */
    private static function is_critical_error( $error_code ) {
        $critical_errors = array(
            'database_error',
            'server_error',
            'authentication_failure',
            'security_violation',
            'data_corruption',
            'system_failure'
        );

        return in_array( $error_code, $critical_errors );
    }

    /**
     * Store critical error for admin notification
     *
     * @param array $error_entry Error entry data
     * @since 1.0.0
     */
    private static function store_critical_error( $error_entry ) {
        $critical_errors = get_option( 'kivicare_critical_errors', array() );
        
        // Add timestamp as key to avoid duplicates
        $critical_errors[ time() ] = $error_entry;
        
        // Keep only last 100 critical errors
        if ( count( $critical_errors ) > 100 ) {
            $critical_errors = array_slice( $critical_errors, -100, null, true );
        }
        
        update_option( 'kivicare_critical_errors', $critical_errors );

        // Send notification to admin if enabled
        if ( get_option( 'kivicare_notify_critical_errors', true ) ) {
            self::notify_admin_critical_error( $error_entry );
        }
    }

    /**
     * Send notification to admin about critical error
     *
     * @param array $error_entry Error entry data
     * @since 1.0.0
     */
    private static function notify_admin_critical_error( $error_entry ) {
        $admin_email = get_option( 'admin_email' );
        if ( ! $admin_email ) {
            return;
        }

        $subject = '[Care API] Critical Error Detected';
        
        $message = "A critical error has occurred in the Care API:\n\n";
        $message .= "Time: {$error_entry['timestamp']}\n";
        $message .= "Error Code: {$error_entry['error_code']}\n";
        $message .= "Error Message: {$error_entry['error_message']}\n";
        $message .= "Request: {$error_entry['request_method']} {$error_entry['request_uri']}\n";
        $message .= "User ID: {$error_entry['user_id']}\n";
        $message .= "IP Address: {$error_entry['user_ip']}\n";
        
        if ( ! empty( $error_entry['error_data'] ) ) {
            $message .= "Additional Data: " . json_encode( $error_entry['error_data'], JSON_PRETTY_PRINT ) . "\n";
        }

        $message .= "\nPlease check the error logs for more details.";

        wp_mail( $admin_email, $subject, $message );
    }

    /**
     * Get error statistics
     *
     * @param int $days Number of days to look back
     * @return array Error statistics
     * @since 1.0.0
     */
    public static function get_error_statistics( $days = 7 ) {
        if ( ! self::$log_file || ! file_exists( self::$log_file ) ) {
            return array();
        }

        $lines = file( self::$log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
        $cutoff_time = strtotime( "-{$days} days" );
        
        $stats = array(
            'total_errors' => 0,
            'error_codes' => array(),
            'hourly_distribution' => array(),
            'top_errors' => array()
        );

        foreach ( $lines as $line ) {
            $error_data = json_decode( $line, true );
            if ( ! $error_data || ! isset( $error_data['timestamp'] ) ) {
                continue;
            }

            $error_time = strtotime( $error_data['timestamp'] );
            if ( $error_time < $cutoff_time ) {
                continue;
            }

            $stats['total_errors']++;

            // Count error codes
            $error_code = $error_data['error_code'] ?? 'unknown';
            if ( ! isset( $stats['error_codes'][$error_code] ) ) {
                $stats['error_codes'][$error_code] = 0;
            }
            $stats['error_codes'][$error_code]++;

            // Hourly distribution
            $hour = date( 'H', $error_time );
            if ( ! isset( $stats['hourly_distribution'][$hour] ) ) {
                $stats['hourly_distribution'][$hour] = 0;
            }
            $stats['hourly_distribution'][$hour]++;
        }

        // Sort and get top errors
        arsort( $stats['error_codes'] );
        $stats['top_errors'] = array_slice( $stats['error_codes'], 0, 10, true );

        return $stats;
    }

    /**
     * Clear error logs
     *
     * @param int $older_than_days Clear logs older than specified days
     * @return bool Success status
     * @since 1.0.0
     */
    public static function clear_error_logs( $older_than_days = 30 ) {
        if ( ! self::$log_file || ! file_exists( self::$log_file ) ) {
            return true;
        }

        $lines = file( self::$log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
        $cutoff_time = strtotime( "-{$older_than_days} days" );
        $kept_lines = array();

        foreach ( $lines as $line ) {
            $error_data = json_decode( $line, true );
            if ( ! $error_data || ! isset( $error_data['timestamp'] ) ) {
                continue;
            }

            $error_time = strtotime( $error_data['timestamp'] );
            if ( $error_time >= $cutoff_time ) {
                $kept_lines[] = $line;
            }
        }

        // Write kept lines back to file
        $content = implode( "\n", $kept_lines );
        if ( ! empty( $content ) ) {
            $content .= "\n";
        }

        return file_put_contents( self::$log_file, $content, LOCK_EX ) !== false;
    }
}