<?php

namespace Clonable\Services;

use Clonable\Helpers\Functions;
use Clonable\Helpers\Json;
use Clonable\Objects\ClonableConfig;
use Clonable\Services\Modules\DataPanelModule;
use Clonable\Services\Modules\ExclusionModule;
use Clonable\Services\Modules\ProductImporterModule;
use Clonable\Services\Modules\TaxonomyModule;
use Clonable\Traits\PluginCheck;
use Clonable\Helpers\TextTranslator;

use WC_Order;

defined('ABSPATH') || exit;

class ClonableWooCommerceService
{
    use PluginCheck;

    private const PDF_REPLACEMENTS_BY_DOMAIN = 'clonable_wpo_replacements_by_domain';
    private const OPT_HEADERS_BY_DOMAIN = 'clonable_email_headers_by_domain';

    public function __construct()
    {
        $module_enabled = get_option('clonable_woocommerce_module_enabled', 'on') === 'on';
        if (!$this->woocommerce_is_installed() || !$module_enabled) {
            return;
        }

        $include_woocommerce_analytics = get_option('clonable_woocommerce_analytics_enabled', 'on') === 'on';
        if ($include_woocommerce_analytics) {
            // Add hidden field with the domain. Will get replaced by Clonable automagically to match actual domain.
            add_action('woocommerce_after_order_notes', array($this, 'add_origin_field'));
            add_action('woocommerce_checkout_update_order_meta', array($this, 'save_origin_field'), 10, 1);
            add_action('woocommerce_admin_order_data_after_shipping_address', array($this, 'display_order_origin'));

            // Implement redirecting back to clone instead of original website
            add_filter('woocommerce_get_checkout_order_received_url', array($this, 'filter_return_url'), 999, 2);

            // Also intercept checkout_url as it is also used by some gateways to handle cancellations.
            add_filter('woocommerce_get_checkout_payment_url', array($this, 'filter_return_url'), 999, 2);

            // Add filter for Mollie's custom return url implementation
            add_filter('mollie-payments-for-woocommerce_return_url', array($this, 'filter_return_url'), 999, 2);

            // Most invoice/cheque/BACS-like gateways use this:
            add_filter('woocommerce_get_return_url', array($this, 'filter_return_url'), 999, 2);

            // Fallback response url handling for certain gateways
            add_filter('woocommerce_payment_successful_result', [$this, 'fallback_return_url'], 999, 2);

            // For gateways (or themes/plugins) that assemble endpoint URLs manually:
            add_filter('woocommerce_get_endpoint_url', [$this, 'manual_fallback_return_url'], 999, 4);
        }

        $include_product_exclusions = get_option('clonable_product_exclusions_enabled', 'on') === 'on';
        if ($include_product_exclusions) {
            $taxonomy_module = new TaxonomyModule();  // registers the exclusion taxonomy and terms.
            $exclusion_module = new ExclusionModule(); // handles the actual product exclusions (also adds checkboxes in admin view).
            $data_panel_module = new DataPanelModule(); // data panel on the admin product page.
            $product_importer_module = new ProductImporterModule(); // enables bulk edit via de WooCommerce product importer
        }

        // Set default checkout country filters (only when user not logged in so we don't interfere with existing user data)
        add_action('plugins_loaded', function () {
            if (!is_user_logged_in()) {
                add_filter('default_checkout_billing_country', array($this, 'get_default_checkout_country'));
                add_filter('default_checkout_shipping_country', function () {
                    return $this->get_default_checkout_country('shipping');
                });
            }
        });

        // Add a hidden field containing the current clone ID for use in woocommerce email translations
        add_action('woocommerce_after_order_notes', array($this, 'add_clone_id_field'), 10, 2);

        // Add the clone ID to the order meta when saving the order   
        add_action('woocommerce_checkout_update_order_meta', array($this, 'save_order_clone_id'));

        // Save clone ID for WooCommerce Blocks checkout (Store API doesn't use $_POST)
        add_action('woocommerce_store_api_checkout_order_processed', array($this, 'save_clone_id_for_block_checkout'), 10, 1);

        // Save origin for WooCommerce Blocks checkout (Store API doesn't use $_POST)
        add_action('woocommerce_store_api_checkout_order_processed', array($this, 'save_origin_field_for_block_checkout'), 20, 1);

        // Support for WooCommerce PDF Invoices & Packing Slips plugin translation
        $pdf_translations_enabled = get_option('clonable_woocommerce_enable_pdf_invoice_translation', 'off') === 'on';
        if ($this->pdf_invoices_installed() && $pdf_translations_enabled) {
            add_action('plugins_loaded', [$this, 'register_pdf_translation_hooks'], 20, 0);
        }
    }

    public function register_pdf_translation_hooks()
    {
        $filter_function_map = [
            "wpo_wcpdf_shop_phone_number_settings_text" => "shop_phone_number",
            "wpo_wcpdf_shop_email_address_settings_text" => "shop_email_address",
            "wpo_wcpdf_shop_name_settings_text" => "shop_name",
            "wpo_wcpdf_vat_number_settings_text" => "vat_number",
            "wpo_wcpdf_coc_number_settings_text" => "coc_number",
            "wpo_wcpdf_shop_address_line_1_settings_text" => "shop_address_line_1",
            "wpo_wcpdf_shop_address_line_2_settings_text" => "shop_address_line_2",
            "wpo_wcpdf_shop_address_country_settings_text" => "shop_address_country",
            "wpo_wcpdf_shop_address_state_settings_text" => "shop_address_state",
            "wpo_wcpdf_shop_address_city_settings_text" => "shop_address_city",
            "wpo_wcpdf_shop_address_postcode_settings_text" => "shop_address_postcode",
        ];

        add_filter('wpo_wcpdf_get_html', function ($html, $document) {
            return $this->get_pdf_template_translation($html, $document);
        }, 10, 2);

        foreach ($filter_function_map as $hook => $field_name) {
            add_filter($hook, function ($text, $document) use ($field_name) {
                return $this->get_pdf_field_replacement($field_name, $text, $document);
            }, 10, 2);
        }

        // Support different logos per language in PDF documents (use multiple filter names for compatibility)
        add_filter('wpo_wcpdf_logo', [$this, 'pdf_logo_url'], 10, 2);
        add_filter('wpo_wcpdf_document_logo', [$this, 'pdf_logo_url'], 10, 2);
        add_filter('wpo_wcpdf_header_logo', [$this, 'pdf_logo_url'], 10, 2);
        add_filter('wpo_wcpdf_template_logo', [$this, 'pdf_logo_url'], 10, 2);

        // logo ID filter
        add_filter('wpo_wcpdf_header_logo_id', [$this, 'pdf_logo_id'], 10, 2);
    }

    /** 
     * Get PDF replacements for a specific clone
     *
     * @param string $clone_id
     * @param string $field_name
     * @param string $default
     *
     * @return string field replacement value or default when not found
     */
    public function get_pdf_field_replacement(string $field_name, string $text, $document): string
    {
        $order = $document->order ?? null;
        $clone_id = $order ? $this->get_clone_id_from_order($order) : '';

        if (!$clone_id) {
            // check if we can get clone ID from the $_POST request
            $clone_id = $clone_id ?? $this->get_clone_id_from_request();
            if (!$clone_id) {
                error_log('No clone ID found for PDF translation');
                return $text;
            }
        }

        $pdf_replacements = $this->get_pdf_replacements_for_clone($clone_id);

        if ($pdf_replacements === null) {
            return $text;
        }

        if (isset($pdf_replacements[$field_name]) && is_string($pdf_replacements[$field_name]) && trim($pdf_replacements[$field_name]) !== '') {
            return $pdf_replacements[$field_name];
        }

        return $text;
    }

    public function get_pdf_template_translation($html, $document)
    {
        $order = $document->order ?? null;
        $clone_id = $order ? $this->get_clone_id_from_order($order) : '';

        if (!$clone_id) {
            // check if we can get clone ID from the $_POST request
            $clone_id = $clone_id ?? $this->get_clone_id_from_request();
            if (!$clone_id) {
                error_log('No clone ID found for PDF translation');
                return $html;
            }
        }

        // make sure element with class shop-info and addresses are not translated by adding notranslate class 
        $html = str_replace('class="shop-info', 'class="shop-info notranslate', $html);
        $html = str_replace('class="address billing-address', 'class="address billing-address notranslate', $html);
        $html = str_replace('class="address shipping-address', 'class="address shipping-address notranslate', $html);

        // Call translation API
        $translated_html = TextTranslator::translateText($html, (string) $clone_id, false) ?? $html;
        return $translated_html ?: $html;
    }

    private function get_clone_id_from_order($orderObject)
    {
        if (!$orderObject || !($orderObject instanceof WC_Order)) {
            return '';
        }
        $clone_id = $orderObject->get_meta('clonable_clone_id');
        return $clone_id ? $clone_id : '';
    }

    /**
     * Filter PDF logo URL based on Clone
     *
     * @param string $logo_url Original logo URL
     * @param mixed $document PDF document object
     * @return string Custom logo URL or original
     */
    public function pdf_logo_url($logo_url, $document)
    {
        $order = $document->order ?? null;
        $clone_id = $order ? $this->get_clone_id_from_order($order) : '';
        if (!$clone_id) {
            $clone_id = $this->get_clone_id_from_request();
        }
        if (!$clone_id) {
            return $logo_url;
        }

        // get the logo URL for the current clone ID 
        $headerInfo = $this->getHeaderInfoForClone($clone_id);
        if ($headerInfo === null) {
            return $logo_url;
        }

        $custom_logo_url = isset($headerInfo['email_logo']) && is_string($headerInfo['email_logo'])
            ? trim($headerInfo['email_logo'])
            : '';

        if ($custom_logo_url !== '') {
            return $custom_logo_url;
        }

        return $logo_url;
    }

    /**
     * Filter PDF logo ID based on Clone 
     *
     * @param int $logo_id Original logo attachment ID
     * @param mixed $document PDF document object
     * @return int Custom logo attachment ID or original
     */
    public function pdf_logo_id($logo_id, $document)
    {
        $order = $document->order ?? null;

        $clone_id = $order ? $this->get_clone_id_from_order($order) : '';
        if (!$clone_id) {
            $clone_id = $this->get_clone_id_from_request();
        }
        if (!$clone_id) {
            return $logo_id;
        }

        // get the logo URL for the current clone ID 
        $headerInfo = $this->getHeaderInfoForClone($clone_id);
        if ($headerInfo === null) {
            return $logo_id;
        }

        $custom_logo_url = isset($headerInfo['email_logo']) && is_string($headerInfo['email_logo'])
            ? trim($headerInfo['email_logo'])
            : '';

        $custom_logo_id = attachment_url_to_postid($custom_logo_url);
        if ($custom_logo_id) {
            return $custom_logo_id;
        }

        return $logo_id;
    }

    public function add_clone_id_field()
    {
        $clone_id = ClonableConfig::get_clone_id_by_current_url();
        echo '<input type="hidden" name="clonable_clone_id" value="' . $clone_id . '" />';
    }


    /**
     * Get the default country (billing or shipping) for the current domain.
     *
     * @param string $type 'billing' or 'shipping'
     * @return string
     */
    public function get_default_checkout_country(?string $type = 'billing')
    {
        $domain_defaults = get_option('clonable_default_country_by_domain');
        if (empty($domain_defaults)) {
            return '';
        }

        $domain_defaults = Json::handle_input($domain_defaults);
        if (empty($domain_defaults) || !is_array($domain_defaults)) {
            return '';
        }

        $current_domain = ClonableConfig::current_clonable_domain();

        foreach ($domain_defaults as $domain_default) {
            if (isset($domain_default->domain) && $domain_default->domain === $current_domain) {
                if ($type === 'billing') {
                    return $domain_default->billing_country ?? '';
                } elseif ($type === 'shipping') {
                    return $domain_default->shipping_country ?? '';
                }
            }
        }
        return '';
    }

    public function add_origin_field($checkout)
    {
        $value = parse_url(get_site_url(), PHP_URL_HOST);
        echo "<input type=\"hidden\" name=\"clonable_origin\" value=\"" . esc_html($value) . "\" />";
    }

    public function save_origin_field($order_id)
    {
        $post_data = wp_unslash($_POST);
        if (!empty($post_data['clonable_origin'])) {
            if ($this->is_valid_origin($post_data['clonable_origin'])) {
                $order = wc_get_order($order_id);
                $order->update_meta_data('clonable_origin', sanitize_text_field($post_data['clonable_origin']));
                $order->save();
            }
        }
    }

    public function save_order_clone_id($order_id)
    {
        $post_data = wp_unslash($_POST);
        if (!empty($post_data['clonable_clone_id'])) {
            $order = wc_get_order($order_id);
            $order->update_meta_data('clonable_clone_id', sanitize_text_field($post_data['clonable_clone_id']));
            $order->save();
        }
    }

    /**
     * Save clone ID for orders placed via the WooCommerce Blocks checkout.
     * The blocks checkout uses the Store API instead of $_POST, so we determine
     * the clone ID server-side from the current request headers.
     *
     * @param WC_Order $order
     */
    public function save_clone_id_for_block_checkout($order)
    {
        // Skip if clone_id was already saved (e.g. classic checkout fallback)
        if ($order->get_meta('clonable_clone_id')) {
            return;
        }
        $clone_id = ClonableConfig::get_clone_id_by_current_url();
        if ($clone_id) {
            $sanitized = sanitize_text_field($clone_id);
            $order->update_meta_data('clonable_clone_id', $sanitized);
            $order->save();
        }
    }

    /**
     * Save order origin for orders placed via the WooCommerce Blocks checkout.
     * The blocks checkout uses the Store API instead of $_POST, so we determine
     * the origin server-side from the current request headers.
     *
     * @param WC_Order $order
     */
    public function save_origin_field_for_block_checkout($order)
    {
        // Skip if origin was already saved (e.g. classic checkout fallback)
        if ($order->get_meta('clonable_origin')) {
            return;
        }

        $origin = null;
        //get the origin clone URL based on the current request headers (set by Clonable on the frontend)
        if (isset($_SERVER['HTTP_CLONABLE_CLONE_DOMAIN'])) {
            $origin = $_SERVER['HTTP_CLONABLE_CLONE_DOMAIN'];
            if (isset($_SERVER['HTTP_CLONABLE_CLONE_SUBFOLDER']) && !empty($_SERVER['HTTP_CLONABLE_CLONE_SUBFOLDER'])) {
                $origin .= $_SERVER['HTTP_CLONABLE_CLONE_SUBFOLDER'];
            }
        } else {
            // fallback to original shop domain when header not present
            $origin = ClonableConfig::ORIGINAL_SHOP;
        }

        $origin = rtrim($origin, '/'); // remove trailing slash from origin if present

        if (!$this->is_valid_origin($origin)) {
            return;
        }

        // save the origin in the order meta
        $order->update_meta_data('clonable_origin', $origin);
        $order->save();
    }

    /**
     * Shows the order origin in the admin overview
     * @param WC_Order $order
     * @return void
     */
    public function display_order_origin($order)
    {
        $clonable_origin = $order->get_meta('clonable_origin');
        echo '<p><strong>Clonable Origin:</strong> ' . esc_html($clonable_origin) . '</p>';
    }

    /** Change the return url to point to the clone site */
    public function filter_return_url($return_url, $order)
    {
        // Check if order is set
        if (!$order) {
            return $return_url;
        }

        // Check if order origin was recorded. No validation is needed here, as it is done when saving the attribute.
        $order_origin = $order->get_meta('clonable_origin');
        $server_data = $_SERVER;
        $host = $server_data['HTTP_HOST'] ?? null;

        if ($order_origin && $host != null) {
            // Make sure both have www. or not
            if (strpos($host, 'www.') !== 0 && strpos($order_origin, 'www.') === 0) {
                // origin has www., host not
                $order_origin = substr($order_origin, 4);
            } else if (strpos($host, 'www.') === 0 && strpos($order_origin, 'www.') !== 0) {
                // host has www., origin not
                $order_origin = 'www.' . $order_origin;
            }

            if (str_contains($return_url, $order_origin)) {
                // if the return url already contains the order origin, then is fine to return
                return $return_url;
            } elseif (Functions::str_starts_with($return_url, '/')) {
                // Starts with slash, prepend domain
                return 'https://' . $order_origin . $return_url;
            } else if (Functions::str_starts_with($return_url, 'http')) {
                // Probably already starts with http, just replace domain
                return str_replace($host, $order_origin, $return_url);
            } else {
                // Some weird scenario, do nothing
                return $return_url;
            }
        } else {
            // Setting not set
            return $return_url;
        }
    }

    /**
     * It's basically the filter_return_url function with an extra check for a redirect option
     * @param $result
     * @param $order_id
     * @return mixed
     */
    public function fallback_return_url($result, $order_id)
    {
        if (!empty($result['redirect'])) {
            $order = $order_id ? wc_get_order($order_id) : null;
            $result['redirect'] = $this->filter_return_url($result['redirect'], $order);
        }
        return $result;
    }

    /**
     * Fallback for generic WooCommerce endpoint URLs.
     * There might be some plugins that build their return URL manually.
     *
     * @param $url
     * @param $endpoint
     * @param $value
     * @param $permalink
     * @return array|mixed|string|string[]
     */
    public function manual_fallback_return_url($url, $endpoint, $value, $permalink)
    {
        $actions = ['order-received', 'thank-you'];
        if (in_array($endpoint, $actions, true)) {
            // If a manual build URL contains one of the action keywords,
            // we assume that it should be filtered for return URls.
            $url = $this->filter_return_url($url, null);
        }
        return $url;
    }

    public function is_valid_origin($origin)
    {
        $input = get_option('clonable_woocommerce_allowed_origins', '[]');
        $allowed = Json::handle_input($input) ?? [];

        if (in_array($origin, $allowed)) {
            return true;
        } else {
            // No direct hit, toggle www.
            if (strpos($origin, 'www.') === 0) {
                return in_array(substr($origin, 4), $allowed);
            } else {
                return in_array('www.' . $origin, $allowed);
            }
        }
    }

    private function get_clone_id_from_request(): string
    {
        return isset($_POST['clonable_clone_id']) ? (string) $_POST['clonable_clone_id'] : '';
    }

    private function get_pdf_replacements_for_clone(string $clone_id): ?array
    {
        foreach ($this->get_pdf_replacements_by_domain() as $domain_pdf_replacements) {
            if (!is_array($domain_pdf_replacements)) {
                continue;
            }

            $db_clone_id = isset($domain_pdf_replacements['clone_id']) ? \trim((string) $domain_pdf_replacements['clone_id']) : '';
            if ($db_clone_id && $clone_id === $db_clone_id) {
                return $domain_pdf_replacements;
            }
        }

        return null;
    }

    private function get_pdf_replacements_by_domain(): array
    {
        $raw = get_option(self::PDF_REPLACEMENTS_BY_DOMAIN, '[]');
        $decoded = json_decode((string) $raw, true);
        return is_array($decoded) ? $decoded : [];
    }


    private function getHeadersByDomain(): array
    {
        $raw = get_option(self::OPT_HEADERS_BY_DOMAIN, '[]');
        $decoded = json_decode((string) $raw, true);
        return is_array($decoded) ? $decoded : [];
    }

    private function getHeaderInfoForClone(string $clone_id): ?array
    {
        foreach ($this->getHeadersByDomain() as $header_info) {
            if (!is_array($header_info)) {
                continue;
            }

            $header_clone_id = isset($header_info['clone_id']) ? trim((string) $header_info['clone_id']) : '';
            if ($header_clone_id !== '' && $header_clone_id === $clone_id) {
                return $header_info;
            }
        }

        return null;
    }
}
