<?php

namespace fluXtore\Core\Runners;

use DateInterval;
use DateTime;
use fluXtore\Core\Admin\Stores\DataStore as FluxDataStore;
use fluXtore\Core\Settings\fluXtore_Settings;

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

if (!class_exists('fluXtore_Analytics_Benchmark')) {
    class fluXtore_Analytics_Benchmark {
        const CRON_HOOK = 'fluxtore_analytics_benchmark_run';
        const OPTION_LAST_RUN = FLUXTORE_PREFIX . 'analytics_last_processed';
        const OPTION_PAYLOAD = FLUXTORE_PREFIX . 'analytics_benchmark_payload';

        public static function init() {
            add_action('fluxtore_init', [__CLASS__, 'bootstrap']);
            add_filter('cron_schedules', [__CLASS__, 'register_schedules']);
            add_action(self::CRON_HOOK, [__CLASS__, 'run']);
            add_action('update_option_' . FLUXTORE_PREFIX . 'analytics_benchmark_schedule', [__CLASS__, 'reschedule']);
            add_action('update_option_' . FLUXTORE_PREFIX . 'analytics_benchmark_schedule_enabled', [__CLASS__, 'reschedule']);
        }

        public static function register_schedules($schedules) {
            if (!isset($schedules['weekly'])) {
                $schedules['weekly'] = [
                    'interval' => WEEK_IN_SECONDS,
                    'display' => __('Once Weekly', 'fluxtore'),
                ];
            }

            return $schedules;
        }

        public static function bootstrap() {
            if (!self::can_run()) {
                self::clear_schedule();

                return;
            }

            self::ensure_schema();
            self::maybe_schedule();
        }

        protected static function can_run() {
            return function_exists('is_fluxtore_pro') && is_fluxtore_pro();
        }

        protected static function maybe_schedule() {
            if (!self::can_run()) {
                return;
            }

            $enabled = (bool) fluXtore_Settings::get('analytics_benchmark_schedule_enabled', true);

            if (!$enabled) {
                self::clear_schedule();

                return;
            }

            $interval = self::normalise_schedule(fluXtore_Settings::get('analytics_benchmark_schedule', 'daily'));

            if (!wp_next_scheduled(self::CRON_HOOK)) {
                wp_schedule_event(time() + HOUR_IN_SECONDS, $interval, self::CRON_HOOK);
            }
        }

        public static function reschedule() {
            if (!self::can_run()) {
                self::clear_schedule();

                return;
            }

            self::clear_schedule();
            self::maybe_schedule();
        }

        protected static function clear_schedule() {
            while ($timestamp = wp_next_scheduled(self::CRON_HOOK)) {
                wp_unschedule_event($timestamp, self::CRON_HOOK);
            }
        }

        protected static function normalise_schedule($value) {
            $allowed = ['hourly', 'twicedaily', 'daily', 'weekly'];

            if (!in_array($value, $allowed, true)) {
                return 'daily';
            }

            return $value;
        }

        protected static function ensure_schema() {
            if (!class_exists(FluxDataStore::class)) {
                return;
            }

            global $wpdb;

            require_once ABSPATH . 'wp-admin/includes/upgrade.php';

            $table = FluxDataStore::get_metrics_table_name();
            $charset = $wpdb->get_charset_collate();

            $sql = "CREATE TABLE {$table} (
                id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
                flow_id bigint(20) unsigned NOT NULL,
                step_id bigint(20) unsigned NOT NULL,
                product_id bigint(20) unsigned DEFAULT 0,
                metric_date date NOT NULL,
                visits int unsigned NOT NULL DEFAULT 0,
                conversions int unsigned NOT NULL DEFAULT 0,
                revenue decimal(20,6) NOT NULL DEFAULT 0,
                created_at datetime NOT NULL,
                updated_at datetime NOT NULL,
                PRIMARY KEY (id),
                UNIQUE KEY flow_step_day (flow_id, step_id, metric_date),
                KEY metric_date (metric_date),
                KEY product_id (product_id)
            ) {$charset};";

            dbDelta($sql);
            FluxDataStore::reset_metrics_table_cache();
        }

        public static function run() {
            if (!self::can_run()) {
                return;
            }

            if (!class_exists(FluxDataStore::class)) {
                return;
            }

            self::ensure_schema();

            $flows = get_posts([
                'post_type' => FLUXTORE_FLOW,
                'post_status' => 'publish',
                'numberposts' => -1,
            ]);

            if (empty($flows)) {
                return;
            }

            $dataStore = new FluxDataStore();

            $lastProcessed = get_option(self::OPTION_LAST_RUN, '');
            $start = $lastProcessed ? new DateTime($lastProcessed) : new DateTime('-30 days');
            if ($lastProcessed) {
                $start->modify('+1 day');
            }
            $start->setTime(0, 0, 0);

            $end = new DateTime('now');
            $end->setTime(0, 0, 0);

            if ($start > $end) {
                $start = clone $end;
            }

            foreach ($flows as $flow) {
                self::process_flow($dataStore, $flow->ID, clone $start, clone $end);
            }

            update_option(self::OPTION_LAST_RUN, $end->format('Y-m-d'));
            self::maybe_store_payload($dataStore, $flows, $start, $end);
        }

        protected static function process_flow(FluxDataStore $dataStore, $flowId, DateTime $start, DateTime $end) {
            $current = clone $start;

            while ($current <= $end) {
                $dayStart = clone $current;
                $dayStart->setTime(0, 0, 0);
                $dayEnd = clone $current;
                $dayEnd->setTime(23, 59, 59);

                $metrics = $dataStore->collect_metrics_snapshot($flowId, $dayStart, $dayEnd);
                self::persist_metrics($flowId, $dayStart, $metrics);

                $current->modify('+1 day');
            }
        }

        protected static function persist_metrics($flowId, DateTime $date, array $metrics) {
            if (empty($metrics)) {
                return;
            }

            global $wpdb;
            $table = FluxDataStore::get_metrics_table_name();
            $day = $date->format('Y-m-d');
            $now = current_time('mysql');

            foreach ($metrics as $stepId => $metric) {
                $wpdb->replace(
                    $table,
                    [
                        'flow_id' => (int) $flowId,
                        'step_id' => (int) $stepId,
                        'product_id' => (int) ($metric['product_id'] ?? 0),
                        'metric_date' => $day,
                        'visits' => (int) ($metric['visits'] ?? 0),
                        'conversions' => (int) ($metric['conversions'] ?? 0),
                        'revenue' => (float) ($metric['revenue'] ?? 0),
                        'created_at' => $now,
                        'updated_at' => $now,
                    ],
                    ['%d', '%d', '%d', '%s', '%d', '%d', '%f', '%s', '%s']
                );
            }
        }

        protected static function maybe_store_payload(FluxDataStore $dataStore, $flows, DateTime $after, DateTime $before) {
            $optIn = (bool) fluXtore_Settings::get('analytics_benchmark_opt_in', false);

            if (!$optIn) {
                delete_option(self::OPTION_PAYLOAD);

                return;
            }

            $payload = [
                'site' => hash('sha256', site_url() . wp_salt('auth')),
                'generated_at' => current_time('mysql'),
                'range' => [
                    'after' => $after->format('Y-m-d'),
                    'before' => $before->format('Y-m-d'),
                ],
                'flows' => [],
            ];

            foreach ($flows as $flow) {
                $summary = $dataStore->get_funnel_conversion_summary($flow->ID, clone $after, clone $before, 'week');
                $products = array_map(
                    static function ($product) {
                        $rawRevenue = isset($product['revenue']) ? (float) $product['revenue'] : 0.0;
                        $formattedRevenue = $product['revenue_formatted'] ?? null;

                        return [
                            'conversions' => (int) ($product['conversions'] ?? 0),
                            'revenue' => $formattedRevenue !== null
                                ? $formattedRevenue
                                : (function_exists('wc_price') ? wc_price($rawRevenue) : $rawRevenue),
                        ];
                    },
                    array_slice($summary['products'], 0, 5)
                );

                $payload['flows'][] = [
                    'flow_id' => $flow->ID,
                    'steps' => array_map(static function ($row) {
                        return [
                            'step_id' => $row['id'],
                            'throughput' => $row['throughput_rate'],
                            'conversion' => $row['conversion_rate'],
                        ];
                    }, $summary['dropoff']),
                    'products' => $products,
                ];
            }

            update_option(self::OPTION_PAYLOAD, $payload, false);
        }
    }
}

fluXtore_Analytics_Benchmark::init();
