<?php
/**
 * Logger — Debug logging engine with smart truncation and file rotation.
 *
 * @package CapiFlow
 */

namespace CapiFlow\Core;

defined( 'ABSPATH' ) || exit;

class Logger {

    /**
     * Log directory path.
     *
     * @var string
     */
    private static $log_dir = '';

    /**
     * Maximum log file age in days.
     *
     * @var int
     */
    const MAX_LOG_AGE_DAYS = 30;

    /**
     * Get the log directory, creating it if necessary.
     *
     * @return string Absolute path with trailing slash.
     */
    private static function get_log_dir() {
        if ( empty( self::$log_dir ) ) {
            $upload_dir  = wp_upload_dir();
            self::$log_dir = trailingslashit( $upload_dir['basedir'] ) . 'capiflow-logs/';

            if ( ! is_dir( self::$log_dir ) ) {
                wp_mkdir_p( self::$log_dir );

                // Protect log directory from direct access.
                $htaccess = self::$log_dir . '.htaccess';
                if ( ! file_exists( $htaccess ) ) {
                    // Using file_put_contents for one-time security file creation (not user-uploaded content).
                    file_put_contents( $htaccess, 'deny from all' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
                }

                $index = self::$log_dir . 'index.html';
                if ( ! file_exists( $index ) ) {
                    // Using file_put_contents for one-time empty index file (directory listing protection).
                    file_put_contents( $index, '' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
                }
            }
        }

        return self::$log_dir;
    }

    /**
     * Get today's log file path.
     *
     * @return string
     */
    private static function get_log_file() {
        return self::get_log_dir() . 'capiflow-' . gmdate( 'Y-m-d' ) . '.log';
    }

    /**
     * Check if debug mode is enabled.
     *
     * @return bool
     */
    public static function is_debug() {
        if ( defined( 'CAPIFLOW_DEBUG' ) && CAPIFLOW_DEBUG ) {
            return true;
        }
        return (bool) get_option( 'capiflow_debug_mode', false );
    }

    /**
     * Log an event to file.
     *
     * Uses Smart Log Truncation:
     * - Debug mode: full payload + response (~3KB per entry)
     * - Production: key fields only (~200 bytes per entry) → 97% disk savings
     *
     * @param string $event_name  Event name.
     * @param string $event_id    Event ID.
     * @param mixed  $payload     Full payload data (array or string).
     * @param int    $http_code   HTTP response code.
     * @param string $response    API response body.
     * @param string $level       Log level: info, warning, error, debug.
     */
    public static function log( $event_name, $event_id = '', $payload = null, $http_code = 0, $response = '', $level = 'info' ) {
        // Only log in production if error or explicitly enabled.
        if ( ! self::is_debug() && 'error' !== $level && 'warning' !== $level ) {
            return;
        }

        $timestamp = gmdate( 'Y-m-d H:i:s' );

        if ( self::is_debug() ) {
            // Debug mode: full payload + response.
            $entry = wp_json_encode( array(
                'ts'       => $timestamp,
                'level'    => $level,
                'event'    => $event_name,
                'id'       => $event_id,
                'http'     => $http_code,
                'payload'  => $payload,
                'response' => $response,
            ) );
        } else {
            // Production: key fields only (~200 bytes).
            $entry = wp_json_encode( array(
                'ts'    => $timestamp,
                'level' => $level,
                'event' => $event_name,
                'id'    => $event_id,
                'http'  => $http_code,
            ) );
        }

        $log_file = self::get_log_file();
        // Using file_put_contents with LOCK_EX for atomic append writes to log files.
        // WP_Filesystem does not support FILE_APPEND | LOCK_EX for concurrent-safe logging.
        file_put_contents( $log_file, $entry . PHP_EOL, FILE_APPEND | LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
    }

    /**
     * Log an error.
     *
     * @param string $event_name Event name.
     * @param string $message    Error message.
     * @param mixed  $context    Additional context data.
     */
    public static function error( $event_name, $message, $context = null ) {
        self::log( $event_name, '', $context, 0, $message, 'error' );
    }

    /**
     * Log a warning.
     *
     * @param string $event_name Event name.
     * @param string $message    Warning message.
     */
    public static function warning( $event_name, $message ) {
        self::log( $event_name, '', null, 0, $message, 'warning' );
    }

    /**
     * Clean up old log files (30+ days).
     *
     * Called via Action Scheduler daily maintenance task.
     */
    public static function cleanup_old_logs() {
        $dir = self::get_log_dir();

        if ( ! is_dir( $dir ) ) {
            return;
        }

        $files   = glob( $dir . 'capiflow-*.log' );
        $cutoff  = time() - ( self::MAX_LOG_AGE_DAYS * DAY_IN_SECONDS );

        if ( ! is_array( $files ) ) {
            return;
        }

        foreach ( $files as $file ) {
            if ( filemtime( $file ) < $cutoff ) {
                wp_delete_file( $file );
            }
        }
    }

    /**
     * Get the last N log entries for the debug console.
     *
     * @param int $count Number of entries to return.
     * @return array Array of log entry objects.
     */
    public static function get_recent_entries( $count = 50 ) {
        $log_file = self::get_log_file();

        if ( ! file_exists( $log_file ) ) {
            return array();
        }

        $lines   = file( $log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
        $entries = array();

        if ( ! is_array( $lines ) ) {
            return array();
        }

        // Get last N lines.
        $lines = array_slice( $lines, -$count );

        foreach ( $lines as $line ) {
            $decoded = json_decode( $line, true );
            if ( is_array( $decoded ) ) {
                $entries[] = $decoded;
            }
        }

        return array_reverse( $entries );
    }
}
