<?php
/**
 * @package         FireBox
 * @version         3.1.1 Pro
 * 
 * @author          FirePlugins <info@fireplugins.com>
 * @link            https://www.fireplugins.com
 * @copyright       Copyright © 2025 FirePlugins All Rights Reserved
 * @license         GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
 */

namespace FireBox\Core\RevenueAttribution;

if (!defined('ABSPATH'))
{
	exit; // Exit if accessed directly.
}

class RevenueAttribution
{
    /** Days to keep attribution cookie valid */
    private $attribution_window = 30;

    public function __construct()
    {
        $this->init();
    }

    /** Register hooks */
    private function init()
    {
        // WooCommerce order completion
        add_action('woocommerce_order_status_completed', [$this, 'track_woocommerce_revenue'], 10, 1);
        add_action('woocommerce_payment_complete', [$this, 'track_woocommerce_revenue'], 10, 1);

        // Easy Digital Downloads payment saved
        add_action('edd_payment_saved', [$this, 'track_edd_revenue'], 10, 2);
    }

    /** WooCommerce attribution */
    public function track_woocommerce_revenue($order_id)
    {
        if (!get_option('firebox_revenue_attribution_auto', false))
        {
            return;
        }
        
        if (\current_user_can('manage_options'))
        {
            return; // Don't track admins
        }
        
        if (!$order_id || !class_exists('WooCommerce'))
        {
            return;
        }

        $order = wc_get_order($order_id);
        if (!$order)
        {
            return;
        }

        $campaigns = $this->get_tracked_campaigns_from_cookie();
        if (empty($campaigns))
        {
            return;
        }

        $results = $this->trackRevenue($campaigns, true, [
            'order_id' => $order_id,
            'order_type' => 'woocommerce',
        ]);
        $this->persistResults($results);
    }

    /** EDD attribution */
    public function track_edd_revenue($payment_id, $payment)
    {
        if (!get_option('firebox_revenue_attribution_auto', false))
        {
            return;
        }

        if (\current_user_can('manage_options'))
        {
            return; // Don't track admins
        }
        
        if (!$payment_id || !function_exists('edd_get_payment'))
        {
            return;
        }

        $payment = edd_get_payment($payment_id);
        if (!$payment)
        {
            return;
        }

        $campaigns = $this->get_tracked_campaigns_from_cookie();
        if (empty($campaigns))
        {
            return;
        }

        $results = $this->trackRevenue($campaigns, true, [
            'order_id' => $payment_id,
            'order_type' => 'edd',
        ]);
        $this->persistResults($results);
    }

    /** Read and validate attribution cookie */
    private function get_tracked_campaigns_from_cookie()
    {
        if (!isset($_COOKIE['firebox_attribution']))
        {
            return [];
        }

        $cookie_data = json_decode(stripslashes($_COOKIE['firebox_attribution']), true);
        if (!is_array($cookie_data))
        {
            return [];
        }

        $cutoff = time() - ($this->attribution_window * 24 * 60 * 60);
        $valid = [];

        foreach ($cookie_data as $campaign)
        {
            if (!isset($campaign['last_seen']) || !isset($campaign['box_id']))
            {
                continue;
            }

            $last_seen = $campaign['last_seen'];
            if ($last_seen > 9999999999)
            { // ms -> s
                $last_seen = $last_seen / 1000;
            }

            if ($last_seen > $cutoff)
            {
                $valid[] = $campaign;
            }
        }

        return $valid;
    }

    /** conversion or impression */
    private function determine_attribution_type($campaign)
    {
        // If the test or caller explicitly provides has_conversion, trust it and avoid DB
        if (array_key_exists('has_conversion', (array) $campaign))
        {
            if ($campaign['has_conversion']) {
                return 'conversion';
            }
        }

        if (isset($campaign['log_id']) && $campaign['log_id'])
        {
            if ($this->db_has_conversion_event_for_log((int) $campaign['log_id']))
            {
                return 'conversion';
            }
        }

        return 'impression';
    }

    private function campaigns_have_conversions($campaigns)
    {
        foreach ($campaigns as $c)
        {
            if ($this->determine_attribution_type($c) === 'conversion')
            {
                return true;
            }
        }
        return false;
    }

    /** Insert a revenue log row */
    private function record_revenue_attribution($box_log_id, $data)
    {
        if ($this->attribution_already_recorded($box_log_id, $data['order_id'], $data['order_type']))
        {
            return;
        }

        $log_data = [
            'log_id' => $box_log_id,
            'event' => 'revenue',
            'event_source' => $data['attribution_type'],
            'event_label' => wp_json_encode([
                'order_id' => $data['order_id'],
                'order_type' => $data['order_type'],
            ]),
            'date' => gmdate('Y-m-d H:i:s'),
        ];

        $this->db_insert_revenue_log($log_data);

        do_action('firebox/revenue_attribution/recorded', $data, $log_data);
    }

    /** Check duplicates */
    private function attribution_already_recorded($box_log_id, $order_id, $order_type)
    {
        return $this->db_has_revenue_for_order($box_log_id, $order_id, $order_type);
    }

    /** Clear cookie */
    private function clear_attribution_cookie()
    {
        setcookie('firebox_attribution', '', time() - 3600, '/');
        unset($_COOKIE['firebox_attribution']);
    }

    /**
     * Compute attribution rows from campaigns.
     * Multi-touch model: All conversions share attribution equally, all impressions share equally
     * Priority: Conversion > Impression
     * Returns: [ [log_id, order_id, order_type, attribution_type], ... ]
     */
    protected function trackRevenue(array $campaigns, $purchased, array $order = [])
    {
        $results = [];
        if (!$purchased || empty($campaigns))
        {
            return $results;
        }

        if ($this->campaigns_have_conversions($campaigns))
        {
            // Multi-touch model: Attribute to ALL conversion campaigns (they split revenue equally)
            foreach ($campaigns as $campaign)
            {
                $type = $this->determine_attribution_type($campaign);
                
                if ($type === 'conversion')
                {
                    $results[] = [
                        'log_id' => $campaign['log_id'],
                        'order_id' => isset($order['order_id']) ? $order['order_id'] : null,
                        'order_type' => isset($order['order_type']) ? $order['order_type'] : null,
                        'attribution_type' => 'conversion',
                    ];
                }
            }
        }
        else
        {
            // No conversions - attribute to all impression campaigns (multi-touch for impressions)
            foreach ($campaigns as $campaign)
            {
                $results[] = [
                    'log_id' => $campaign['log_id'],
                    'order_id' => isset($order['order_id']) ? $order['order_id'] : null,
                    'order_type' => isset($order['order_type']) ? $order['order_type'] : null,
                    'attribution_type' => 'impression',
                ];
            }
        }

        return $results;
    }

    /** Insert results and clear cookie */
    private function persistResults(array $results)
    {
        foreach ($results as $row)
        {
            $this->record_revenue_attribution($row['log_id'], $row);
        }

        $this->clear_attribution_cookie();
    }

    /**
     * Check if there is a conversion event for a given log id.
     * Default implementation queries the DB.
     */
    protected function db_has_conversion_event_for_log(int $log_id): bool
    {
        global $wpdb;
        if (!($wpdb instanceof \wpdb))
        {
            return false;
        }
        $table = $wpdb->prefix . 'firebox_logs_details';
        $query = $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table} WHERE log_id = %d AND event = 'conversion'",
            $log_id
        );
        return (int) $wpdb->get_var($query) > 0;
    }

    /**
     * Check if there is a click event for a given log id.
     * Default implementation queries the DB.
     */
    protected function db_has_click_event_for_log(int $log_id): bool
    {
        global $wpdb;
        if (!($wpdb instanceof \wpdb))
        {
            return false;
        }
        $table = $wpdb->prefix . 'firebox_logs_details';
        $query = $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table} WHERE log_id = %d AND event = 'click'",
            $log_id
        );
        return (int) $wpdb->get_var($query) > 0;
    }

    /** Insert a revenue log row into DB */
    protected function db_insert_revenue_log(array $log_data): void
    {
        global $wpdb;
        if (!($wpdb instanceof \wpdb))
        {
            return;
        }
        $table = $wpdb->prefix . 'firebox_logs_details';
        $wpdb->insert($table, $log_data);
    }

    /** Check if a revenue row for this order is already recorded */
    protected function db_has_revenue_for_order($box_log_id, $order_id, $order_type): bool
    {
        global $wpdb;
        if (!($wpdb instanceof \wpdb))
        {
            return false;
        }
        $table = $wpdb->prefix . 'firebox_logs_details';
        $query = $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table} WHERE log_id = %d AND event = 'revenue' AND JSON_EXTRACT(event_label, '$.order_id') = %s AND JSON_EXTRACT(event_label, '$.order_type') = %s",
            $box_log_id,
            $order_id,
            $order_type
        );
        return (int) $wpdb->get_var($query) > 0;
    }
}