<?php

namespace BookWellAB;

defined('ABSPATH') || exit;

// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Plugin-owned table names from get_table_name() + esc_sql(); user values use $wpdb->prepare().

if (!class_exists('BookWellAB_Payment_Debug_Log')) {

    class BookWellAB_Payment_Debug_Log
    {

        public const LOGS_VIEW_PER_PAGE = 10;

        private static $instance = null;

        /** @var string[] */
        private static $secret_keys = [
            'client_secret',
            'access_token',
            'authorization',
            'spam_code',
            'spam_key',
            'password',
            'smtp_password',
        ];

        /**
         * @return self
         */
        public static function get_instance()
        {
            if (self::$instance === null) {
                self::$instance = new self();
            }
            return self::$instance;
        }

        public function __construct()
        {
            add_action('wp_ajax_bookwellab_get_payment_debug_logs', [$this, 'ajax_get_logs']);
            add_action('wp_ajax_bookwellab_clear_payment_debug_logs', [$this, 'ajax_clear_logs']);
            add_action('wp_ajax_bookwellab_download_payment_debug_logs', [$this, 'ajax_download_logs']);
        }

        /**
         * Registered payment gateways for debug logging UI.
         *
         * @return array<int, array{slug: string, label: string, setting_key: string}>
         */
        public static function get_gateways()
        {
            $gateways = [
                [
                    'slug' => 'offline',
                    'label' => __('On Site method', 'bookwellnow-appointment-booking'),
                    'setting_key' => 'enable_offline_log',
                ],
                [
                    'slug' => 'paypal',
                    'label' => __('PayPal method', 'bookwellnow-appointment-booking'),
                    'setting_key' => 'enable_paypal_log',
                ],
            ];

            /**
             * Filter payment debug log gateway definitions for settings UI and logging.
             *
             * @param array<int, array{slug: string, label: string, setting_key: string}> $gateways Gateway definitions.
             */
            return apply_filters('bookwellab_payment_debug_log_gateways', $gateways);
        }

        /**
         * Email notification debug log channel.
         *
         * @return array{slug: string, label: string, setting_key: string}
         */
        public static function get_email_log_channel()
        {
            return [
                'slug' => 'email',
                'label' => __('Email Notification Logs', 'bookwellnow-appointment-booking'),
                'setting_key' => 'enable_email_log',
            ];
        }

        /**
         * All log channels (payment gateways + email).
         *
         * @return array<int, array{slug: string, label: string, setting_key: string}>
         */
        public static function get_all_log_channels()
        {
            $channels = self::get_gateways();
            $channels[] = self::get_email_log_channel();
            return $channels;
        }

        /**
         * Download range options for admin UI.
         *
         * @return array<int, array{value: string, label: string}>
         */
        public static function get_download_range_options()
        {
            return [
                ['value' => '1_day', 'label' => __('Last 1 Day', 'bookwellnow-appointment-booking')],
                ['value' => '3_days', 'label' => __('Last 3 Days', 'bookwellnow-appointment-booking')],
                ['value' => '1_week', 'label' => __('Last 1 Week', 'bookwellnow-appointment-booking')],
                ['value' => '2_weeks', 'label' => __('Last 2 Weeks', 'bookwellnow-appointment-booking')],
                ['value' => '1_month', 'label' => __('Last Month', 'bookwellnow-appointment-booking')],
                ['value' => 'all', 'label' => __('All', 'bookwellnow-appointment-booking')],
                ['value' => 'custom', 'label' => __('Custom', 'bookwellnow-appointment-booking')],
            ];
        }

        /**
         * @param string $gateway Gateway slug.
         * @return bool
         */
        public static function is_enabled($gateway)
        {
            $gateway = sanitize_key($gateway);
            $setting_key = self::get_setting_key_for_gateway($gateway);
            if ($setting_key === '') {
                return false;
            }

            $settings = BookWellAB_Settings::get_settings();
            $debug_log = isset($settings['debug_log']) && is_array($settings['debug_log']) ? $settings['debug_log'] : [];

            return isset($debug_log[$setting_key]) && $debug_log[$setting_key] === 'yes';
        }

        /**
         * @param string $gateway Gateway slug.
         * @param string $event   Event name.
         * @param string $event_from Source context.
         * @param mixed  $data    Payload.
         * @param int    $ref_id  Optional appointment ID.
         * @return int|false Log ID or false when skipped.
         */
        public static function log($gateway, $event, $event_from, $data, $ref_id = 0)
        {
            if (!self::is_enabled($gateway)) {
                return false;
            }

            global $wpdb;

            $bwdb = new BookWellAB_Database();
            $table = esc_sql($bwdb->get_table_name('payment_debug_logs'));

            $sanitized_data = self::redact_secrets($data);
            $json = wp_json_encode($sanitized_data);
            if (!is_string($json)) {
                $json = '{}';
            }

            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Plugin-owned debug log insert.
            $inserted = $wpdb->insert(
                $table,
                [
                    'bw_log_ref_id' => absint($ref_id),
                    'bw_log_gateway' => sanitize_key($gateway),
                    'bw_log_event' => sanitize_text_field((string) $event),
                    'bw_log_event_from' => sanitize_text_field((string) $event_from),
                    'bw_log_data' => $json,
                    'bw_log_created_at' => current_time('mysql'),
                ],
                ['%d', '%s', '%s', '%s', '%s', '%s']
            );

            return $inserted ? (int) $wpdb->insert_id : false;
        }

        /**
         * @param string               $gateway Gateway slug.
         * @param array<string, mixed> $args    Query args: page, per_page.
         * @return array{items: array<int, array<string, mixed>>, total: int}
         */
        public static function get_logs($gateway, $args = [])
        {
            global $wpdb;

            $gateway = sanitize_key($gateway);
            if ($gateway === '') {
                return ['items' => [], 'total' => 0];
            }

            $page = isset($args['page']) ? max(1, absint($args['page'])) : 1;
            $per_page = isset($args['per_page']) ? max(1, min(100, absint($args['per_page']))) : self::LOGS_VIEW_PER_PAGE;
            $offset = ($page - 1) * $per_page;

            $bwdb = new BookWellAB_Database();
            $table = esc_sql($bwdb->get_table_name('payment_debug_logs'));

            $total = (int) $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT COUNT(*) FROM {$table} WHERE bw_log_gateway = %s",
                    $gateway
                )
            );

            $rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT bw_log_id, bw_log_ref_id, bw_log_gateway, bw_log_event, bw_log_event_from, bw_log_data, bw_log_created_at
                     FROM {$table}
                     WHERE bw_log_gateway = %s
                     ORDER BY bw_log_id DESC
                     LIMIT %d OFFSET %d",
                    $gateway,
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );

            $items = [];
            if (is_array($rows)) {
                foreach ($rows as $row) {
                    $items[] = self::format_log_row($row);
                }
            }

            return [
                'items' => $items,
                'total' => $total,
            ];
        }

        /**
         * @param string $gateway Gateway slug.
         * @return int Rows deleted.
         */
        public static function clear_logs($gateway)
        {
            global $wpdb;

            $gateway = sanitize_key($gateway);
            if ($gateway === '') {
                return 0;
            }

            $bwdb = new BookWellAB_Database();
            $table = esc_sql($bwdb->get_table_name('payment_debug_logs'));

            return (int) $wpdb->query(
                $wpdb->prepare(
                    "DELETE FROM {$table} WHERE bw_log_gateway = %s",
                    $gateway
                )
            );
        }

        /**
         * @param string $gateway     Gateway slug.
         * @param string $range       Range key.
         * @param string $custom_from Custom start date (Y-m-d).
         * @param string $custom_to   Custom end date (Y-m-d).
         * @return array<int, array<string, mixed>>
         */
        public static function get_logs_for_download($gateway, $range, $custom_from = '', $custom_to = '')
        {
            global $wpdb;

            $gateway = sanitize_key($gateway);
            if ($gateway === '') {
                return [];
            }

            $bounds = self::resolve_date_bounds($range, $custom_from, $custom_to);
            $bwdb = new BookWellAB_Database();
            $table = esc_sql($bwdb->get_table_name('payment_debug_logs'));

            if ($bounds['from'] !== '' && $bounds['to'] !== '') {
                $rows = $wpdb->get_results(
                    $wpdb->prepare(
                        "SELECT bw_log_id, bw_log_ref_id, bw_log_gateway, bw_log_event, bw_log_event_from, bw_log_data, bw_log_created_at
                         FROM {$table}
                         WHERE bw_log_gateway = %s AND bw_log_created_at >= %s AND bw_log_created_at <= %s
                         ORDER BY bw_log_id DESC",
                        $gateway,
                        $bounds['from'] . ' 00:00:00',
                        $bounds['to'] . ' 23:59:59'
                    ),
                    ARRAY_A
                );
            } else {
                $rows = $wpdb->get_results(
                    $wpdb->prepare(
                        "SELECT bw_log_id, bw_log_ref_id, bw_log_gateway, bw_log_event, bw_log_event_from, bw_log_data, bw_log_created_at
                         FROM {$table}
                         WHERE bw_log_gateway = %s
                         ORDER BY bw_log_id DESC",
                        $gateway
                    ),
                    ARRAY_A
                );
            }

            $items = [];
            if (is_array($rows)) {
                foreach ($rows as $row) {
                    $items[] = self::format_log_row($row);
                }
            }

            return $items;
        }

        /**
         * Build a sanitized booking summary for debug logs.
         *
         * @param array<string, mixed> $booking_data Booking payload.
         * @return array<string, mixed>
         */
        public static function summarize_booking_data($booking_data)
        {
            if (!is_array($booking_data)) {
                return [];
            }

            $customer = isset($booking_data['customer']) && is_array($booking_data['customer']) ? $booking_data['customer'] : [];
            $email = isset($customer['email']) ? sanitize_email((string) $customer['email']) : '';

            return [
                'service_id' => isset($booking_data['service_id']) ? absint($booking_data['service_id']) : 0,
                'staff_id' => isset($booking_data['staff_id']) ? absint($booking_data['staff_id']) : 0,
                'date' => isset($booking_data['date']) ? sanitize_text_field((string) $booking_data['date']) : '',
                'time' => isset($booking_data['time']) ? sanitize_text_field((string) $booking_data['time']) : '',
                'payment_gateway' => isset($booking_data['payment_gateway']) ? sanitize_key((string) $booking_data['payment_gateway']) : '',
                'service_price' => isset($booking_data['service_price']) ? (float) $booking_data['service_price'] : 0,
                'customer_email' => $email,
            ];
        }

        /**
         * @return void
         */
        public function ajax_get_logs()
        {
            if (!BookWellAB_Permissions::has_permission('bookwellab_manage_settings')) {
                wp_send_json_error(['msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $gateway = sanitize_key(BookWellAB_Input::get_post_string('gateway'));
            if (!self::is_valid_gateway($gateway)) {
                wp_send_json_error(['msg' => esc_html__('Invalid debug log channel.', 'bookwellnow-appointment-booking')]);
            }

            $page = absint(BookWellAB_Input::get_post_string('page'));
            if ($page <= 0) {
                $page = 1;
            }

            $per_page = absint(BookWellAB_Input::get_post_string('per_page'));
            if ($per_page <= 0) {
                $per_page = self::LOGS_VIEW_PER_PAGE;
            }

            $result = self::get_logs($gateway, [
                'page' => $page,
                'per_page' => $per_page,
            ]);

            wp_send_json_success($result);
        }

        /**
         * @return void
         */
        public function ajax_clear_logs()
        {
            if (!BookWellAB_Permissions::has_permission('bookwellab_manage_settings')) {
                wp_send_json_error(['msg' => esc_html__('Unauthorized', 'bookwellnow-appointment-booking')]);
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_send_json_error(['msg' => esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking')]);
            }

            $gateway = sanitize_key(BookWellAB_Input::get_post_string('gateway'));
            if (!self::is_valid_gateway($gateway)) {
                wp_send_json_error(['msg' => esc_html__('Invalid debug log channel.', 'bookwellnow-appointment-booking')]);
            }

            self::clear_logs($gateway);

            wp_send_json_success([
                'msg' => esc_html__('Debug logs cleared successfully.', 'bookwellnow-appointment-booking'),
            ]);
        }

        /**
         * @return void
         */
        public function ajax_download_logs()
        {
            if (!BookWellAB_Permissions::has_permission('bookwellab_manage_settings')) {
                wp_die(esc_html__('Unauthorized', 'bookwellnow-appointment-booking'));
            }

            if (!check_ajax_referer('bw_wp_nonce', '_wpnonce', false)) {
                wp_die(esc_html__('Security token mismatch.', 'bookwellnow-appointment-booking'));
            }

            $gateway = sanitize_key(BookWellAB_Input::get_post_string('gateway'));
            if (!self::is_valid_gateway($gateway)) {
                wp_die(esc_html__('Invalid debug log channel.', 'bookwellnow-appointment-booking'));
            }

            $range = sanitize_key(BookWellAB_Input::get_post_string('range'));
            if ($range === '') {
                $range = '1_week';
            }

            $custom_from = sanitize_text_field(BookWellAB_Input::get_post_string('custom_from'));
            $custom_to = sanitize_text_field(BookWellAB_Input::get_post_string('custom_to'));

            $items = self::get_logs_for_download($gateway, $range, $custom_from, $custom_to);
            $filename = 'bookwellab-' . $gateway . '-debug-' . gmdate('Y-m-d-His') . '.log';

            nocache_headers();
            header('Content-Type: text/plain; charset=utf-8');
            header('Content-Disposition: attachment; filename="' . $filename . '"');

            foreach ($items as $item) {
                echo '----------------------------------------' . "\n";
                echo 'Log ID: ' . (int) $item['log_id'] . "\n";
                echo 'Gateway: ' . esc_html($item['gateway']) . "\n";
                echo 'Event: ' . esc_html($item['event']) . "\n";
                echo 'Source: ' . esc_html($item['event_from']) . "\n";
                echo 'Appointment ID: ' . (int) $item['ref_id'] . "\n";
                echo 'Date: ' . esc_html($item['created_at']) . "\n";
                echo 'Data: ' . esc_html($item['data']) . "\n\n";
            }

            exit;
        }

        /**
         * @param string $gateway Gateway slug.
         * @return string
         */
        private static function get_setting_key_for_gateway($gateway)
        {
            foreach (self::get_all_log_channels() as $gateway_row) {
                if ($gateway_row['slug'] === $gateway) {
                    return $gateway_row['setting_key'];
                }
            }
            return '';
        }

        /**
         * @param string $gateway Gateway slug.
         * @return bool
         */
        private static function is_valid_gateway($gateway)
        {
            foreach (self::get_all_log_channels() as $gateway_row) {
                if ($gateway_row['slug'] === $gateway) {
                    return true;
                }
            }
            return false;
        }

        /**
         * @param array<string, mixed> $row DB row.
         * @return array<string, mixed>
         */
        private static function format_log_row($row)
        {
            $data = isset($row['bw_log_data']) ? (string) $row['bw_log_data'] : '';

            return [
                'log_id' => isset($row['bw_log_id']) ? (int) $row['bw_log_id'] : 0,
                'ref_id' => isset($row['bw_log_ref_id']) ? (int) $row['bw_log_ref_id'] : 0,
                'gateway' => isset($row['bw_log_gateway']) ? sanitize_key((string) $row['bw_log_gateway']) : '',
                'event' => isset($row['bw_log_event']) ? sanitize_text_field((string) $row['bw_log_event']) : '',
                'event_from' => isset($row['bw_log_event_from']) ? sanitize_text_field((string) $row['bw_log_event_from']) : '',
                'data' => $data,
                'created_at' => isset($row['bw_log_created_at']) ? sanitize_text_field((string) $row['bw_log_created_at']) : '',
            ];
        }

        /**
         * @param string $range       Range key.
         * @param string $custom_from Custom start date.
         * @param string $custom_to   Custom end date.
         * @return array{from: string, to: string}
         */
        private static function resolve_date_bounds($range, $custom_from, $custom_to)
        {
            $range = sanitize_key($range);
            $today = current_time('Y-m-d');

            if ($range === 'custom') {
                $from = preg_match('/^\d{4}-\d{2}-\d{2}$/', $custom_from) ? $custom_from : '';
                $to = preg_match('/^\d{4}-\d{2}-\d{2}$/', $custom_to) ? $custom_to : '';
                return ['from' => $from, 'to' => $to];
            }

            if ($range === 'all') {
                return ['from' => '', 'to' => ''];
            }

            $days_map = [
                '1_day' => 1,
                '3_days' => 3,
                '1_week' => 7,
                '2_weeks' => 14,
                '1_month' => 30,
            ];

            if (!isset($days_map[$range])) {
                $range = '1_week';
            }

            $from_ts = strtotime('-' . (int) $days_map[$range] . ' days', strtotime($today . ' 00:00:00'));

            return [
                'from' => $from_ts ? wp_date('Y-m-d', $from_ts) : $today,
                'to' => $today,
            ];
        }

        /**
         * @param mixed $data Raw payload.
         * @return mixed
         */
        private static function redact_secrets($data)
        {
            if (is_array($data)) {
                $clean = [];
                foreach ($data as $key => $value) {
                    $key_string = is_string($key) ? strtolower($key) : '';
                    if ($key_string !== '' && self::is_secret_key($key_string)) {
                        $clean[$key] = '[REDACTED]';
                        continue;
                    }
                    $clean[$key] = self::redact_secrets($value);
                }
                return $clean;
            }

            if (is_object($data)) {
                return self::redact_secrets((array) $data);
            }

            return $data;
        }

        /**
         * @param string $key Lowercase key name.
         * @return bool
         */
        private static function is_secret_key($key)
        {
            foreach (self::$secret_keys as $secret_key) {
                if ($key === $secret_key || strpos($key, $secret_key) !== false) {
                    return true;
                }
            }
            return false;
        }
    }
}

// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
