<?php

namespace Soft_Tehnica\Freya_Sync_Service\Services;

use Soft_Tehnica\Freya_Sync_Service\Data\FsPickupMethod;
use Soft_Tehnica\Freya_Sync_Service\Data\FsShippingMethod;
use Soft_Tehnica\Freya_Sync_Service\STUtils\STView;
use WC_Shipping_Method;
use WC_Shipping_Zones;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

class CartService
{
    public string $modalAddressContent;
    public string $needAddressAlert;
    public ?string $countyRestriction;
    public ?string $companyUid;
    public ?string $defaultLocationUid;
    public array $addressOnPages = [];
    public $addressRequestOnPages;
    public $enableMultiSubdomain;
    public $isRunningOnLandingPageInstance;
    public bool $isRestApi = false;
    public bool $isSingleProductPage = false;
    public ?string $noAddressFoundMessage;
    public bool $noPolygons;
    public bool $needToAskAddressOnLoad = true;
    public bool $triggerModalAddressAuto = true;

    private bool $fsShippingEnabled = false;
    private bool $fsPickupEnabled = false;

    public function __construct()
    {
        $this->setLocalData();
        $this->init();
        $zones = WC_Shipping_Zones::get_zones();
        foreach ($zones as $zone) {
            /** @var WC_Shipping_Method $method */
            foreach ($zone["shipping_methods"] as $method) {
                if ($method->id === "fs_pickup_method" && $method->enabled === "yes") {
                    $this->fsPickupEnabled = true;
                }
                if ($method->id === "fs_shipping_method" && $method->enabled === "yes") {
                    $this->fsShippingEnabled = true;
                }
            }
        }

        SessionService::get()->updateSessionWithActiveShipping(
            $this->fsShippingEnabled,
            $this->fsPickupEnabled
        );
        $availableLocations = LocationService::get()->getAvaliableLocations(
            PluginSettings::get()->isEnableMultiSubdomain()
        );
        SessionService::get()->setFetchedLocations($availableLocations);

        $this->modalAddressContent = $this->renderModal();
        $this->needAddressAlert = $this->renderNeedAddressContent();
        AddressService::get()->setCartLocationFromSession();

        add_shortcode("fss_need_address", [$this, "need_address_shortcode"]);
    }

    public function need_address_shortcode(): string
    {
        return $this->needAddressAlert;
    }

    public function setLocalData(): void
    {
        $options = get_option('fs_option_settings', array());

        $this->companyUid = (isset($options['fsCompanyUid'])) ? $options['fsCompanyUid'] : null;
        $this->defaultLocationUid = (isset($options['fsDefaultLocationUid'])) ? $options['fsDefaultLocationUid'] : null;
        $this->addressOnPages = (isset($options['fsAddressRequestOnPages'])) ? $options['fsAddressRequestOnPages'] : [];
        $this->enableMultiSubdomain = (isset($options['fsEnableMultiSubdomain'])) ? $options['fsEnableMultiSubdomain'] : null;
        $this->isRunningOnLandingPageInstance = (isset($options['fsIsRunningOnLandingPageInstance'])) ? $options['fsIsRunningOnLandingPageInstance'] : null;
        $this->countyRestriction = (isset($options['fsCountyRestriction'])) ? $options['fsCountyRestriction'] : null;
        $this->noAddressFoundMessage = (isset($options['fsNoAddressFoundMessage'])) ? $options['fsNoAddressFoundMessage'] : null;
        $this->noPolygons = isset($options['fsNoPolygons']) && $options['fsNoPolygons'] === 1;
    }

    public function renderNeedAddressContent(): string
    {
        return STView::render('need-address', [
            'address' => SessionService::get()->getUserAddress(),
            'pickup' => SessionService::get()->isPickup(),
            'hasDelivery' => $this->fsShippingEnabled,
            'locationName' => SessionService::get()->getLocationName()
        ]);
    }

    public function renderModal(): string
    {
        return STView::render('modal-address', [
            'noAddressMessage' => $this->noAddressFoundMessage,
            'pickup' => SessionService::get()->isPickup(),
            'userAddress' => SessionService::get()->getUserAddress(),
            'hasPickup' => $this->fsPickupEnabled,
            'hasDelivery' => $this->fsShippingEnabled,
            'activeLocationUid' => SessionService::get()->getLocationUid(),
            'locations' => SessionService::get()->getFetchedLocations()
        ]);
    }

    public function validateCartAddress(): bool
    {
        if (SessionService::get()->isPickup()) {
            return true;
        }
        $userAddress = SessionService::get()->getUserAddress();
        return isset($userAddress) && strlen($userAddress) > 0;
    }

    public function validateCartProduct($id): bool
    {
        $locationUid = SessionService::get()->getLocationUid();

        if (!isset($locationUid)) {
            return false;
        }

        $meta = get_post_meta($id, 'location_uid', true);

        if ($meta && $meta === $locationUid) {
            return true;
        }

        return false;
    }

    public function validateCardItem(
        $passed,
        $product_id = null,
        $quantity = null,
        $variation_id = '',
        $variations = ''
    ): bool {
        if (!$this->validateCartAddress()) {
            wc_add_notice('Nu aveți o adresă de livrare selectată', 'error');
            return false;
        }
        if (!$this->validateCartProduct($product_id)) {
            wc_add_notice(
                __(
                    'Acest produs nu se poate livra la adresa selectata anterior!',
                    'textdomain'
                ),
                'error'
            );

            return false;
        }
        return $passed;
    }

    public function finalHtmlResponse($wpPageContent): string
    {
        $content = $wpPageContent . $this->modalAddressContent;
        $pageId = get_queried_object_id();

        $modalDismissed = SessionService::get()->isAddressModalDismissed();

        $userAddressDetails = SessionService::get()->getUserAddressInputs();

        $PAGE_SHOULD_SHOW_MODAL = in_array(
            $pageId,
            PluginSettings::get()->getPagesThatRequestAddress()
        );
        $COMPANY_HAS_POLYGONS = PluginSettings::get()->isPolygonsDisabled();
        $USER_ADDRESS_SET = isset($userAddressDetails);

        if (
            $PAGE_SHOULD_SHOW_MODAL &&
            $this->triggerModalAddressAuto &&
            !$modalDismissed &&
            $COMPANY_HAS_POLYGONS &&
            !$USER_ADDRESS_SET
        ) {
            $content .= "<script> const j = jQuery.noConflict(); j.('#modal').addClass('modal-visible') </script>";
        }

        return $content;
    }

    public function setMinimOrderValue(): void
    {
        $woocommerce = WC();

        if (is_admin() && !defined('DOING_AJAX')) {
            return;
        }

        $taxData = SessionService::get()->getTax();

        if (is_null($taxData) || $taxData->minimumFee === 0.) {
            return;
        }

        if ((float)$woocommerce->cart->get_displayed_subtotal() < $taxData->minimumFee) {
            $value = number_format($taxData->minimumFee, 2);
            $diffValue = number_format(
                (float)abs(
                    (float)$woocommerce->cart->get_displayed_subtotal() - $taxData->minimumFee
                ),
                2
            );


            $message = "Comanda minima pentru adresa dumneavoastra este in valoare de: $value RON. <br> Mai trebuie sa adaugi produse in valoare de $diffValue RON pentru a finaliza comanda.";

            wc_clear_notices();
            wc_add_notice(__($message, 'textdomain'), 'error');
        }
    }

    public function destroySession(): void
    {
        SessionService::get()->clearSession();
    }

    public function initRestApi($args): void
    {
        if ($args) {
            $this->isRestApi = true;
        }
    }

    public function filterProductsByLocation($query): void
    {
        // Check this is main query and other conditionals as needed
        if ($query->is_main_query()) {
            $query->set(
                'meta_query',
                array(
                    array(
                        'key' => 'location_uid',
                        'compare' => 'IN',
                        'value' => SessionService::get()->getLocationUid() ?? PluginSettings::get()->getDefaultLocationUid()
                    )
                )
            );
        }
    }

    /**
     * @ajax cart_service_service_modal_dismissed
     *
     * @return void
     */
    public function setModalDismissed(): void
    {
        SessionService::get()->setAddressModalDismissed(true);
    }

    public function onOrderPlaced($id): void
    {
        $order = wc_get_order($id);

        $this->destroySession();
    }

    public function onWoocommerceBillingFields(array $fields): array
    {
        $fields["billing"]["billing_email"]["required"] = false;

        return $fields;
    }

    public function customizeCheckoutAddressFields(array $fields): array
    {
        if (SessionService::get()->isPickup()) {
            $fields["address_1"]["required"] = false;
            $fields["country"]["required"] = false;
            $fields["city"]["required"] = false;
            $fields["state"]["required"] = false;
            $fields["postcode"]["required"] = false;
            return $fields;
        }
        $city = null;
        $address = SessionService::get()->getAddress();
        $userInputs = SessionService::get()->getUserAddressInputs();
        if ($address) {
            if ($address->suburb !== '') {
                $city = $address->suburb;
            }
            if ($address->county !== "") {
                $city = $address->county;
            }
            if ($address->city !== "") {
                $city = $address->city;
            }
        }
        if (!$city) {
            $city = 'Neidentificat';
        }

        $fields["street_number"] = [
            "type" => "text",
            "label" => "Număr stradă",
            "priority" => 61,
            "default" => $address->streetNumber ?? "",
            "value" => $address->streetNumber ?? "",
        ];
        if ($address) {
            $fields["city"]["custom_attributes"] = [
                "readonly" => "readonly"
            ];
            $fields["city"]["default"] = $city;
            $fields["city"]["value"] = $city;
            unset($fields["city"]["autocomplete"]);

            $fields["address_1"]["custom_attributes"] = [
                "readonly" => "readonly"
            ];
            $fields["address_1"]["default"] = $address->displayName;
            $fields["address_1"]["value"] = $address->displayName;
            unset($fields["address_1"]["autocomplete"]);
            unset($fields["address_2"]);
        }
        $fields["unit"] = [
            "type" => "text",
            "label" => "Bloc",
            "priority" => 62,
            "default" => $userInputs->block ?? "",
            "value" => $userInputs->block ?? ""
        ];
        $fields["staircase"] = [
            "type" => "text",
            "label" => "Scară",
            "priority" => 63,
            "default" => $userInputs->entrance ?? "",
            "value" => $userInputs->entrance ?? "",
        ];
        $fields["floor"] = [
            "type" => "text",
            "label" => "Etaj",
            "priority" => 64,
            "default" => $userInputs->floor ?? "",
            "value" => $userInputs->floor ?? "",
        ];
        $fields["apartment"] = [
            "type" => "text",
            "label" => "Apartament",
            "priority" => 65,
            "default" => $userInputs->apartment ?? "",
            "value" => $userInputs->apartment ?? "",
        ];

        return $fields;
    }

    public function processPaymentGateways($pMethods)
    {
        return $pMethods;
    }

    private function getLocationNameFromAPI(): ?string
    {
        $res = makeHttpGet(
            "api/wp/getLocation/?uid={$this->defaultLocationUid}"
        );
        if (isset($res->payload->name)) {
            return $res->payload->name;
        }
        return null;
    }

    public function displayFieldsOnProductPage(): void
    {
        $this->displayProductCommentary();
    }

    private function displayProductCommentary(): void
    {
        woocommerce_form_field(
            'freya_sync_commentary',
            array(
                'type' => 'textarea',
                'class' => array('freya_sync_commentary', 'form-row-wide'),
                'label' => __('Comentariu', 'woocommerce'),
                'id' => 'freya_sync_commentary',
            )
        );
    }

    public function prepare_notices(): void
    {
        echo $this->needAddressAlert;
    }

    public function addProductFieldsToCartItem(
        $cart_item_data,
        $product_id
    ): array {
        if (isset($_POST['freya_sync_commentary'])) {
            $cart_item_data['freya_sync_commentary'] = sanitize_text_field(
                $_POST['freya_sync_commentary']
            );
        }

        return $cart_item_data;
    }

    public function addProductFieldsToOrderItem(
        $item,
        $cart_item_key,
        $values,
        $order
    ) {
        if (isset($values['freya_sync_commentary'])) {
            $item->add_meta_data(
                'freya_sync_commentary',
                $values['freya_sync_commentary'],
                true
            );
        }

        return $item;
    }

    public function validateCartOnSubmit($fields, $errors): void
    {
        if ($this->noPolygons || SessionService::get()->isPickup()) {
            return;
        }

        $userAddress = SessionService::get()->getUserAddress();
        if (!$userAddress || strlen($userAddress) === 0) {
            $errors->add(
                'validation',
                'Adresa de livrare nu este setată! <b style="cursor: pointer" class="address-modal-open">Te rugăm să selectezi o adresă de livrare</b>.'
            );
        }
    }

    public function onOrderCreated($order): void
    {
        /** @var \WC_Order $order */
        $locationName = SessionService::get()->getLocationName();
        $locationUid = SessionService::get()->getLocationUid() ?? PluginSettings::get()->getDefaultLocationUid();
        if ($this->noPolygons) {
            $locationName = $this->getLocationNameFromAPI();
        }

        $items = $order->get_items();

        foreach ($items as $item) {
            $data = $item->get_data();
            if (isset($data['freya_sync_toppings'])) {
                $item->add_meta_data(
                    'freya_sync_toppings',
                    $data['freya_sync_toppings'],
                    true
                );
                $item->save();
                $item->save_meta_data();
            }
        }

        $address = SessionService::get()->getAddress() ?? null;
        if (isset($address)) {
            $order->update_meta_data('freya-sync-address-id', $address->id);
            $order->update_meta_data('freya-sync-address-type', $address->type);
        }

        if (!isset($locationName) || !isset($locationUid)) {
            $order->update_meta_data(
                'freya-sync-error',
                'Nu am putut asocia aceasta comandă unei locații'
            );
            $order->update_meta_data('freya-sync-processed', 'false');
            return;
        }
        $order->update_meta_data('location_uid', $locationUid);
        $order->update_meta_data('location_name', $locationName);
        $order->update_meta_data('freya-sync-uid', '');
        $order->update_meta_data('freya-order-id', '');
        $order->update_meta_data('freya-sync-error', '');
        $order->update_meta_data('freya-sync-processed', '');
    }

    public function getOrdersApiRoute(WP_REST_Request $request)
    {
        if (is_user_logged_in()) {
            return new WP_REST_Response('ok', 200);
        }

        return new WP_Error(
            'unauthorized',
            __('You shall not pass'),
            ['status' => 401]
        ); //can also use WP_REST_Response
    }

    public function ajaxClearSession(): void
    {
        $this->destroySession();
        wp_send_json_success();
    }

    public function registerShippingMethod($methods): array
    {
        $methods['fs_shipping_method'] = new FsShippingMethod();
        $methods['fs_pickup_method'] = new FsPickupMethod();

        return $methods;
    }

    public function init(): void
    {
        if (isset($_GET['redirect-payload'])) {
            AddressService::get()->setLocationSettingsMultisite(
                $_GET['redirect-payload']
            );
        }
        if (!$this->noPolygons) {
            add_action(
                'wp_ajax_nopriv_cart_clear_session',
                [$this, 'ajaxClearSession'],
                1
            );
            add_action(
                'wp_ajax_cart_clear_session',
                [$this, 'ajaxClearSession'],
                1
            );

            // Create public route for address modal dismissed
            add_action(
                'wp_ajax_nopriv_cart_service_service_modal_dismissed',
                [$this, 'setModalDismissed'],
                1
            );
            add_action(
                'wp_ajax_cart_service_service_modal_dismissed',
                [$this, 'setModalDismissed'],
                1
            );

            // Validate card
            add_filter(
                'woocommerce_add_to_cart_validation',
                [$this, 'validateCardItem'],
                1,
                5
            );

            // Add extra taxes to cart
            add_action(
                'woocommerce_cart_calculate_fees',
                [$this, 'setMinimOrderValue'],
                1
            );

            // Register shipping method
            add_filter(
                'woocommerce_shipping_methods',
                [$this, 'registerShippingMethod'],
                1,
                1
            );

            // Hook on edit billing address
            add_filter(
                'woocommerce_checkout_fields',
                [$this, 'onWoocommerceBillingFields'],
                10,
                2
            );
            add_filter(
                "woocommerce_default_address_fields",
                [$this, "customizeCheckoutAddressFields"]
            );

            add_filter(
                'woocommerce_after_checkout_validation',
                [$this, 'validateCartOnSubmit'],
                10,
                2
            );

            add_action(
                'woocommerce_before_shop_loop',
                [$this, 'prepare_notices'],
                10
            );
            add_action(
                'woocommerce_before_single_product_summary',
                [$this, 'prepare_notices'],
                10
            );

            add_filter('pre_get_posts', function ($query) {
                if (!is_admin() && (is_shop() || is_search() || is_product_category())) {
                    $this->filterProductsByLocation($query);
                }
            });

            add_action('template_redirect', function () {
                if ($this->needToAskAddressOnLoad && $this->isRestApi === false) {
                    ob_start([$this, 'finalHtmlResponse']);
                }
            });
        }
        // Add extra fields on product page

        add_action(
            'woocommerce_before_add_to_cart_button',
            array($this, 'displayFieldsOnProductPage')
        );
        add_filter(
            'woocommerce_add_cart_item_data',
            array($this, 'addProductFieldsToCartItem'),
            11,
            2
        );
        add_filter(
            'woocommerce_checkout_create_order_line_item',
            array($this, 'addProductFieldsToOrderItem'),
            11,
            4
        );

        add_action('woocommerce_init', array($this, 'startSession'));
        add_action(
            'woocommerce_available_payment_gateways',
            [$this, 'processPaymentGateways'],
            10,
            1
        );

        // Detect if is a rest call api
        add_action('rest_api_init', [$this, 'initRestApi']);

        add_filter(
            'woocommerce_checkout_create_order',
            [$this, 'onOrderCreated'],
            10,
            1
        );

        // Action on order is placed - unset session
        add_action('woocommerce_thankyou', [$this, 'onOrderPlaced'], 10, 1);
    }
}
