<?php
/**
 * API Logger Utility
 *
 * Comprehensive logging system for API operations with WordPress integration
 *
 * @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_REST_Request;
use WP_REST_Response;

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

/**
 * Class API_Logger
 *
 * Centralized logging system for all API operations
 *
 * @since 1.0.0
 */
class API_Logger {

    /**
     * Log levels
     */
    const LOG_LEVEL_DEBUG = 1;
    const LOG_LEVEL_INFO = 2;
    const LOG_LEVEL_WARNING = 3;
    const LOG_LEVEL_ERROR = 4;
    const LOG_LEVEL_CRITICAL = 5;

    /**
     * Log file paths
     *
     * @var array
     */
    private static $log_files = array();

    /**
     * Current log level
     *
     * @var int
     */
    private static $log_level = self::LOG_LEVEL_INFO;

    /**
     * Initialize logger
     *
     * @since 1.0.0
     */
    public static function init() {
        $upload_dir = wp_upload_dir();
        $log_dir = $upload_dir['basedir'] . '/care-api-logs';

        // Ensure log directory exists
        if ( ! file_exists( $log_dir ) ) {
            wp_mkdir_p( $log_dir );
        }

        // Set log file paths
        self::$log_files = array(
            'api' => $log_dir . '/api-requests.log',
            'auth' => $log_dir . '/authentication.log',
            'performance' => $log_dir . '/performance.log',
            'security' => $log_dir . '/security.log',
            'database' => $log_dir . '/database.log',
            'business' => $log_dir . '/business-logic.log'
        );

        // Set log level from options
        self::$log_level = get_option( 'care_api_log_level', self::LOG_LEVEL_INFO );

        // Add request/response logging hooks
        add_action( 'rest_api_init', array( __CLASS__, 'setup_request_logging' ) );
    }

    /**
     * Setup request logging hooks
     *
     * @since 1.0.0
     */
    public static function setup_request_logging() {
        add_filter( 'rest_pre_dispatch', array( __CLASS__, 'log_request_start' ), 10, 3 );
        add_filter( 'rest_post_dispatch', array( __CLASS__, 'log_request_end' ), 10, 3 );
    }

    /**
     * Log API request start
     *
     * @param mixed           $result  Response to replace the requested version with.
     * @param WP_REST_Server  $server  Server instance.
     * @param WP_REST_Request $request Request object.
     * @return mixed
     * @since 1.0.0
     */
    public static function log_request_start( $result, $server, $request ) {
        // Only log Care API requests
        $route = $request->get_route();
        if ( strpos( $route, '/care/v1/' ) === false ) {
            return $result;
        }

        // Store request start time
        $GLOBALS['care_api_request_start'] = microtime( true );

        // Log request details
        self::log_api_request( $request );

        return $result;
    }

    /**
     * Log API request end
     *
     * @param WP_REST_Response $result  Response object.
     * @param WP_REST_Server   $server  Server instance.
     * @param WP_REST_Request  $request Request object.
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function log_request_end( $result, $server, $request ) {
        // Only log Care API requests
        $route = $request->get_route();
        if ( strpos( $route, '/care/v1/' ) === false ) {
            return $result;
        }

        // Calculate response time
        $request_time = 0;
        if ( isset( $GLOBALS['care_api_request_start'] ) ) {
            $request_time = ( microtime( true ) - $GLOBALS['care_api_request_start'] ) * 1000; // Convert to milliseconds
        }

        // Log response details
        self::log_api_response( $request, $result, $request_time );

        return $result;
    }

    /**
     * Log API request
     *
     * @param WP_REST_Request $request Request object
     * @since 1.0.0
     */
    public static function log_api_request( WP_REST_Request $request ) {
        if ( self::$log_level > self::LOG_LEVEL_DEBUG ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'type' => 'request',
            'method' => $request->get_method(),
            'route' => $request->get_route(),
            'params' => $request->get_params(),
            'headers' => $request->get_headers(),
            'user_id' => get_current_user_id(),
            'ip_address' => self::get_client_ip(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        );

        // Remove sensitive data from logs
        $log_data['params'] = self::sanitize_log_data( $log_data['params'] );
        $log_data['headers'] = self::sanitize_log_data( $log_data['headers'] );

        self::write_log( 'api', $log_data );
    }

    /**
     * Log API response
     *
     * @param WP_REST_Request  $request      Request object
     * @param WP_REST_Response $response     Response object
     * @param float            $request_time Request time in milliseconds
     * @since 1.0.0
     */
    public static function log_api_response( WP_REST_Request $request, WP_REST_Response $response, $request_time = 0 ) {
        $status_code = $response->get_status();
        $log_level = self::LOG_LEVEL_INFO;

        // Determine log level based on status code
        if ( $status_code >= 400 && $status_code < 500 ) {
            $log_level = self::LOG_LEVEL_WARNING;
        } elseif ( $status_code >= 500 ) {
            $log_level = self::LOG_LEVEL_ERROR;
        }

        if ( self::$log_level > $log_level ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'type' => 'response',
            'method' => $request->get_method(),
            'route' => $request->get_route(),
            'status_code' => $status_code,
            'response_time_ms' => round( $request_time, 2 ),
            'response_size_bytes' => strlen( json_encode( $response->get_data() ) ),
            'user_id' => get_current_user_id(),
            'ip_address' => self::get_client_ip(),
        );

        // Add response data for errors or debug mode
        if ( $status_code >= 400 || self::$log_level === self::LOG_LEVEL_DEBUG ) {
            $log_data['response_data'] = $response->get_data();
            $log_data['response_data'] = self::sanitize_log_data( $log_data['response_data'] );
        }

        self::write_log( 'api', $log_data );

        // Log performance data
        if ( $request_time > 1000 ) { // Log slow requests (> 1 second)
            self::log_performance_issue( $request, $request_time );
        }
    }

    /**
     * Log authentication events
     *
     * @param string $event       Event type (login, logout, token_refresh, etc.)
     * @param int    $user_id     User ID
     * @param bool   $success     Success status
     * @param string $error_code  Error code if failed
     * @param array  $extra_data  Additional data
     * @since 1.0.0
     */
    public static function log_auth_event( $event, $user_id = 0, $success = true, $error_code = '', $extra_data = array() ) {
        $log_level = $success ? self::LOG_LEVEL_INFO : self::LOG_LEVEL_WARNING;
        
        if ( self::$log_level > $log_level ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'event' => $event,
            'user_id' => $user_id,
            'success' => $success,
            'ip_address' => self::get_client_ip(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        );

        if ( ! $success && $error_code ) {
            $log_data['error_code'] = $error_code;
        }

        if ( ! empty( $extra_data ) ) {
            $log_data['extra_data'] = self::sanitize_log_data( $extra_data );
        }

        self::write_log( 'auth', $log_data );
    }

    /**
     * Log performance issues
     *
     * @param WP_REST_Request $request      Request object
     * @param float           $request_time Request time in milliseconds
     * @since 1.0.0
     */
    public static function log_performance_issue( WP_REST_Request $request, $request_time ) {
        if ( self::$log_level > self::LOG_LEVEL_WARNING ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'type' => 'slow_request',
            'method' => $request->get_method(),
            'route' => $request->get_route(),
            'response_time_ms' => round( $request_time, 2 ),
            'user_id' => get_current_user_id(),
            'ip_address' => self::get_client_ip(),
            'memory_usage' => memory_get_usage( true ),
            'peak_memory' => memory_get_peak_usage( true ),
        );

        self::write_log( 'performance', $log_data );
    }

    /**
     * Log security events
     *
     * @param string $event_type Event type
     * @param string $description Event description
     * @param array  $details     Additional details
     * @since 1.0.0
     */
    public static function log_security_event( $event_type, $description, $details = array() ) {
        if ( self::$log_level > self::LOG_LEVEL_WARNING ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'event_type' => $event_type,
            'description' => $description,
            'user_id' => get_current_user_id(),
            'ip_address' => self::get_client_ip(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
            'request_uri' => $_SERVER['REQUEST_URI'] ?? '',
        );

        if ( ! empty( $details ) ) {
            $log_data['details'] = self::sanitize_log_data( $details );
        }

        self::write_log( 'security', $log_data );
    }

    /**
     * Log database operations
     *
     * @param string $operation Operation type (select, insert, update, delete)
     * @param string $table     Table name
     * @param float  $exec_time Execution time in milliseconds
     * @param int    $rows      Number of rows affected
     * @param string $error     Error message if any
     * @since 1.0.0
     */
    public static function log_database_operation( $operation, $table, $exec_time = 0, $rows = 0, $error = '' ) {
        $log_level = $error ? self::LOG_LEVEL_ERROR : self::LOG_LEVEL_DEBUG;
        
        if ( self::$log_level > $log_level ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'operation' => $operation,
            'table' => $table,
            'execution_time_ms' => round( $exec_time, 2 ),
            'rows_affected' => $rows,
            'user_id' => get_current_user_id(),
        );

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

        self::write_log( 'database', $log_data );

        // Log slow queries
        if ( $exec_time > 100 ) { // Log queries slower than 100ms
            self::log_performance_issue_db( $operation, $table, $exec_time );
        }
    }

    /**
     * Log business logic events
     *
     * @param string $event       Event type
     * @param string $description Event description
     * @param array  $context     Context data
     * @since 1.0.0
     */
    public static function log_business_event( $event, $description, $context = array() ) {
        if ( self::$log_level > self::LOG_LEVEL_INFO ) {
            return;
        }

        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'event' => $event,
            'description' => $description,
            'user_id' => get_current_user_id(),
            'ip_address' => self::get_client_ip(),
        );

        if ( ! empty( $context ) ) {
            $log_data['context'] = self::sanitize_log_data( $context );
        }

        self::write_log( 'business', $log_data );
    }

    /**
     * Log critical system events
     *
     * @param string $event       Event type
     * @param string $description Event description
     * @param array  $details     Additional details
     * @since 1.0.0
     */
    public static function log_critical_event( $event, $description, $details = array() ) {
        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'level' => 'CRITICAL',
            'event' => $event,
            'description' => $description,
            'user_id' => get_current_user_id(),
            'ip_address' => self::get_client_ip(),
            'memory_usage' => memory_get_usage( true ),
            'peak_memory' => memory_get_peak_usage( true ),
        );

        if ( ! empty( $details ) ) {
            $log_data['details'] = self::sanitize_log_data( $details );
        }

        // Always write critical events regardless of log level
        self::write_log( 'api', $log_data );

        // Also log to WordPress error log
        $message = "[Care API CRITICAL] {$event}: {$description}";
        if ( ! empty( $details ) ) {
            $message .= ' - ' . json_encode( $details );
        }
        error_log( $message );

        // Send email notification for critical events
        self::notify_critical_event( $log_data );
    }

    /**
     * Write log entry to file
     *
     * @param string $log_type Log type (api, auth, performance, etc.)
     * @param array  $log_data Log data
     * @since 1.0.0
     */
    private static function write_log( $log_type, $log_data ) {
        if ( ! isset( self::$log_files[$log_type] ) ) {
            return;
        }

        $log_file = self::$log_files[$log_type];
        $log_entry = json_encode( $log_data ) . "\n";

        // Write to file with file locking
        $result = file_put_contents( $log_file, $log_entry, FILE_APPEND | LOCK_EX );

        // Rotate log if it gets too large (> 10MB)
        if ( $result !== false && file_exists( $log_file ) && filesize( $log_file ) > 10485760 ) {
            self::rotate_log_file( $log_file );
        }
    }

    /**
     * Rotate log file
     *
     * @param string $log_file Log file path
     * @since 1.0.0
     */
    private static function rotate_log_file( $log_file ) {
        $rotated_file = $log_file . '.' . date( 'Y-m-d-H-i-s' );
        
        if ( rename( $log_file, $rotated_file ) ) {
            // Compress rotated file
            if ( function_exists( 'gzencode' ) ) {
                $content = file_get_contents( $rotated_file );
                $compressed = gzencode( $content );
                file_put_contents( $rotated_file . '.gz', $compressed );
                unlink( $rotated_file );
            }

            // Clean up old rotated files (keep last 5)
            $log_dir = dirname( $log_file );
            $log_name = basename( $log_file );
            $pattern = $log_dir . '/' . $log_name . '.*';
            
            $files = glob( $pattern );
            if ( count( $files ) > 5 ) {
                usort( $files, function( $a, $b ) {
                    return filemtime( $a ) - filemtime( $b );
                });
                
                $files_to_delete = array_slice( $files, 0, -5 );
                foreach ( $files_to_delete as $file ) {
                    unlink( $file );
                }
            }
        }
    }

    /**
     * Sanitize log data to remove sensitive information
     *
     * @param mixed $data Data to sanitize
     * @return mixed Sanitized data
     * @since 1.0.0
     */
    private static function sanitize_log_data( $data ) {
        if ( is_array( $data ) ) {
            $sensitive_keys = array(
                'password', 'token', 'authorization', 'auth_token', 'access_token',
                'refresh_token', 'api_key', 'secret', 'private_key', 'jwt'
            );

            foreach ( $data as $key => $value ) {
                $key_lower = strtolower( $key );
                if ( in_array( $key_lower, $sensitive_keys ) || strpos( $key_lower, 'password' ) !== false ) {
                    $data[$key] = '[REDACTED]';
                } elseif ( is_array( $value ) ) {
                    $data[$key] = self::sanitize_log_data( $value );
                }
            }
        }

        return $data;
    }

    /**
     * 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';
    }

    /**
     * Log database performance issue
     *
     * @param string $operation Operation type
     * @param string $table     Table name
     * @param float  $exec_time Execution time in milliseconds
     * @since 1.0.0
     */
    private static function log_performance_issue_db( $operation, $table, $exec_time ) {
        $log_data = array(
            'timestamp' => current_time( 'Y-m-d H:i:s' ),
            'type' => 'slow_query',
            'operation' => $operation,
            'table' => $table,
            'execution_time_ms' => round( $exec_time, 2 ),
            'user_id' => get_current_user_id(),
        );

        self::write_log( 'performance', $log_data );
    }

    /**
     * Notify about critical events
     *
     * @param array $log_data Log data
     * @since 1.0.0
     */
    private static function notify_critical_event( $log_data ) {
        if ( ! get_option( 'care_api_notify_critical', false ) ) {
            return;
        }

        $admin_email = get_option( 'admin_email' );
        if ( ! $admin_email ) {
            return;
        }

        $subject = '[Care API] Critical Event Alert';
        $message = "A critical event has occurred:\n\n";
        $message .= "Event: {$log_data['event']}\n";
        $message .= "Description: {$log_data['description']}\n";
        $message .= "Time: {$log_data['timestamp']}\n";
        $message .= "User ID: {$log_data['user_id']}\n";
        $message .= "IP: {$log_data['ip_address']}\n";

        if ( isset( $log_data['details'] ) ) {
            $message .= "Details: " . json_encode( $log_data['details'], JSON_PRETTY_PRINT );
        }

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

    /**
     * Get log statistics
     *
     * @param string $log_type Log type
     * @param int    $days     Number of days to analyze
     * @return array Statistics
     * @since 1.0.0
     */
    public static function get_log_statistics( $log_type = 'api', $days = 7 ) {
        if ( ! isset( self::$log_files[$log_type] ) || ! file_exists( self::$log_files[$log_type] ) ) {
            return array();
        }

        $log_file = self::$log_files[$log_type];
        $lines = file( $log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
        $cutoff_time = strtotime( "-{$days} days" );
        
        $stats = array(
            'total_entries' => 0,
            'entries_by_hour' => array(),
            'average_response_time' => 0,
            'slowest_requests' => array(),
            'status_codes' => array(),
            'top_routes' => array(),
            'error_count' => 0
        );

        $response_times = array();

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

            $entry_time = strtotime( $entry['timestamp'] );
            if ( $entry_time < $cutoff_time ) {
                break;
            }

            $stats['total_entries']++;

            // Hour distribution
            $hour = date( 'H', $entry_time );
            if ( ! isset( $stats['entries_by_hour'][$hour] ) ) {
                $stats['entries_by_hour'][$hour] = 0;
            }
            $stats['entries_by_hour'][$hour]++;

            // Response time analysis
            if ( isset( $entry['response_time_ms'] ) ) {
                $response_times[] = $entry['response_time_ms'];
                
                if ( $entry['response_time_ms'] > 1000 ) {
                    $stats['slowest_requests'][] = array(
                        'route' => $entry['route'] ?? '',
                        'method' => $entry['method'] ?? '',
                        'time' => $entry['response_time_ms'],
                        'timestamp' => $entry['timestamp']
                    );
                }
            }

            // Status codes
            if ( isset( $entry['status_code'] ) ) {
                $code = $entry['status_code'];
                if ( ! isset( $stats['status_codes'][$code] ) ) {
                    $stats['status_codes'][$code] = 0;
                }
                $stats['status_codes'][$code]++;

                if ( $code >= 400 ) {
                    $stats['error_count']++;
                }
            }

            // Route popularity
            if ( isset( $entry['route'] ) ) {
                $route = $entry['route'];
                if ( ! isset( $stats['top_routes'][$route] ) ) {
                    $stats['top_routes'][$route] = 0;
                }
                $stats['top_routes'][$route]++;
            }
        }

        // Calculate averages
        if ( ! empty( $response_times ) ) {
            $stats['average_response_time'] = round( array_sum( $response_times ) / count( $response_times ), 2 );
        }

        // Sort and limit results
        arsort( $stats['top_routes'] );
        $stats['top_routes'] = array_slice( $stats['top_routes'], 0, 10, true );

        usort( $stats['slowest_requests'], function( $a, $b ) {
            return $b['time'] - $a['time'];
        });
        $stats['slowest_requests'] = array_slice( $stats['slowest_requests'], 0, 10 );

        return $stats;
    }

    /**
     * Set log level
     *
     * @param int $level Log level
     * @since 1.0.0
     */
    public static function set_log_level( $level ) {
        self::$log_level = $level;
        update_option( 'care_api_log_level', $level );
    }

    /**
     * Get current log level
     *
     * @return int Current log level
     * @since 1.0.0
     */
    public static function get_log_level() {
        return self::$log_level;
    }

    /**
     * Clear logs older than specified days
     *
     * @param int $days Days to keep
     * @return array Results for each log type
     * @since 1.0.0
     */
    public static function cleanup_logs( $days = 30 ) {
        $results = array();
        $cutoff_time = strtotime( "-{$days} days" );

        foreach ( self::$log_files as $type => $file ) {
            $results[$type] = self::cleanup_log_file( $file, $cutoff_time );
        }

        return $results;
    }

    /**
     * Cleanup single log file
     *
     * @param string $file        Log file path
     * @param int    $cutoff_time Cutoff timestamp
     * @return array Result data
     * @since 1.0.0
     */
    private static function cleanup_log_file( $file, $cutoff_time ) {
        if ( ! file_exists( $file ) ) {
            return array( 'success' => true, 'entries_removed' => 0, 'entries_kept' => 0 );
        }

        $lines = file( $file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
        $kept_lines = array();
        $removed_count = 0;

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

            $entry_time = strtotime( $entry['timestamp'] );
            if ( $entry_time >= $cutoff_time ) {
                $kept_lines[] = $line;
            } else {
                $removed_count++;
            }
        }

        $content = implode( "\n", $kept_lines );
        if ( ! empty( $content ) ) {
            $content .= "\n";
        }

        $success = file_put_contents( $file, $content, LOCK_EX ) !== false;

        return array(
            'success' => $success,
            'entries_removed' => $removed_count,
            'entries_kept' => count( $kept_lines )
        );
    }
}