<?php

namespace BexioConnectorByHelvy;

/**
 * Admin API Settings
 *
 * @since 0.1.0
 */

defined('ABSPATH') || exit;

class AdminAPISettings
{
    public static $prefix = 'bexio_connector_by_helvy_';

    public function __construct()
    {
        add_action('rest_api_init', function () {
            // Payment gateways
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/woocommerce', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_woocommerce_settings'),
                'permission_callback' => array($this, 'get_permission')
            ));

            // Bexio settings
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/bexio/', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_bexio_settings'),
                'permission_callback' => array($this, 'get_permission')
            ));

            // Settings
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_settings'),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/', array(
                'methods' => 'POST',
                'callback' => array($this, 'set_settings'),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/auto-setup/', array(
                'methods' => 'POST',
                'callback' => array($this, 'perform_auto_setup'),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/auto-setup/is-done', array(
                'methods' => 'GET',
                'callback' => array($this, 'is_auto_setup_done'),
                'permission_callback' => array($this, 'get_permission')
            ));

            // Subscription & Payment
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/subscriptions', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_current_subscriptions'),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/subscriptions', array(
                'methods' => 'PATCH',
                'callback' => array($this, 'update_current_subscriptions'),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/subscription-plans', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_subscription_plans'),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/payment-checkout/(?P<variantId>\d+)', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_checkout_url'),
                'args' => array(
                    'variantId' => array(
                        'validate_callback' => function ($param, $request, $key) {
                            return is_numeric($param);
                        }
                    ),
                ),
                'permission_callback' => array($this, 'get_permission')
            ));
            register_rest_route(BEXIO_CONNECTOR_BY_HELVY_REST_API_ROUTE, '/settings/payment-portal', array(
                'methods' => 'GET',
                'callback' => array($this, 'get_customer_portal_url'),
                'permission_callback' => array($this, 'get_permission')
            ));
        });

        $this->init_additional_settings();
    }

    /**
     * Initialize additional settings with default values (if not already set in wp_options)
     */
    private function init_additional_settings() {
        // Remap ignore_payment_sync_for_payment_methods (string[] -> array[])
        if ($ignore_payment_sync_for_payment_methods = get_option(self::$prefix . 'ignore_payment_sync_for_payment_methods')) {
            if (is_array($ignore_payment_sync_for_payment_methods) && !empty($ignore_payment_sync_for_payment_methods) && is_string($ignore_payment_sync_for_payment_methods[0])) {
                $ignore_payment_sync_for_payment_methods = array_map(function ($e) {
                    return ['paymentMethod' => $e];
                }, $ignore_payment_sync_for_payment_methods);
                update_option(self::$prefix . 'ignore_payment_sync_for_payment_methods', $ignore_payment_sync_for_payment_methods);
            }
        }

        // Remap attach_bexio_invoice_to_woocommerce_email if true: boolean -> array of statuses (default ["customer_processing_order", "customer_on_hold_order"])
        if ($attach_bexio_invoice_to_woocommerce_email = get_option(self::$prefix . 'attach_bexio_invoice_to_woocommerce_email')) {
            if (($attach_bexio_invoice_to_woocommerce_email === true || $attach_bexio_invoice_to_woocommerce_email === 1 || $attach_bexio_invoice_to_woocommerce_email === "1")
                && get_option(self::$prefix . 'attach_bexio_invoice_to_woocommerce_email_statuses') === false) {
                update_option(self::$prefix . 'attach_bexio_invoice_to_woocommerce_email_statuses', ["customer_processing_order", "customer_on_hold_order"]);
            }
        }

        // Remap sync_orders -> sync_orders_statuses
        if (get_option(self::$prefix . 'sync_orders') && get_option(self::$prefix . 'sync_orders_statuses') === false) {
            update_option(self::$prefix . 'sync_orders_statuses', ["pending", "on-hold", "processing", "completed"]);
            update_option(self::$prefix . 'auto_create_delivery_notes', true);
        }

        // Remap auto_create_invoices -> sync_invoices_statuses
        if (get_option(self::$prefix . 'auto_create_invoices') && get_option(self::$prefix . 'sync_invoices_statuses') === false) {
            update_option(self::$prefix . 'sync_invoices_statuses', ["pending", "on-hold", "processing", "completed"]);
        }

        // Remap auto_issue_invoices -> sync_issue_invoices_statuses
        if (get_option(self::$prefix . 'auto_issue_invoices') && get_option(self::$prefix . 'sync_issue_invoices_statuses') === false) {
            update_option(self::$prefix . 'sync_issue_invoices_statuses', ["pending", "on-hold", "processing", "completed"]);
        }

        // Remap sync_fulfillments -> sync_fulfillments_statuses
        if (get_option(self::$prefix . 'sync_fulfillments') && get_option(self::$prefix . 'sync_fulfillments_statuses') === false) {
            update_option(self::$prefix . 'sync_fulfillments_statuses', ["completed"]);
        }

        // Add default settings
        $default_settings = array(
            self::$prefix . 'auto_create_delivery_notes' => true,
            self::$prefix . 'attach_bexio_invoice_to_woocommerce_document_name' => "invoice-{woocommerce_order_number}",
            self::$prefix . 'attach_bexio_order_to_woocommerce_document_name' => "order-{woocommerce_order_number}",
        );

        foreach ($default_settings as $key => $value) {
            if (false === get_option($key)) {
                add_option($key, $value);
            }
        }
    }

    private function allowed_html_settings()
    {
        return array();
    }

    private function registered_settings()
    {
        $options = array(
            self::$prefix . 'invoice_title_format', // String,
            self::$prefix . 'invoice_header_text', // String,
            self::$prefix . 'invoice_payment_header_text', // String,
            self::$prefix . 'custom_payment_method_translations', // ,
            self::$prefix . 'invoice_footer_text', // String,
            self::$prefix . 'invoice_line_item_format', // String,
            self::$prefix . 'invoice_line_add_sh_code', // Boolean,
            self::$prefix . 'invoice_shipping_line_text', // String,
            self::$prefix . 'invoice_discount_line_text', // String,
            self::$prefix . 'sync_products', // Boolean,
            self::$prefix . 'sync_products_description', // Boolean,
            self::$prefix . 'sync_stocks', // String,
            
            self::$prefix . 'sync_orders', // Boolean (deprecated),
            self::$prefix . 'sync_orders_statuses', // [String],
            self::$prefix . 'auto_create_delivery_notes', // Boolean,
            self::$prefix . 'sync_zero_orders', // Boolean,
            
            self::$prefix . 'auto_create_invoices', // Boolean (deprecated),
            self::$prefix . 'sync_invoices_statuses', // [String],
            
            self::$prefix . 'auto_issue_invoices', // Boolean (deprecated),
            self::$prefix . 'sync_issue_invoices_statuses', // [String],
            
            self::$prefix . 'sync_fulfillments', // Boolean (deprecated),
            self::$prefix . 'sync_fulfillments_statuses', // [String],

            self::$prefix . 'sync_payments', // Boolean,
            self::$prefix . 'sync_orders_only_for_payment_methods', // String,

            self::$prefix . 'sync_paid_status_from_bexio_to_woocommerce', // Boolean,
            self::$prefix . 'sync_fulfilled_status_from_bexio_to_woocommerce', // Boolean,
            self::$prefix . 'attach_bexio_invoice_to_woocommerce_email', // Boolean (deprecated),
            self::$prefix . 'attach_bexio_invoice_to_woocommerce_email_statuses', // [String],
            self::$prefix . 'attach_bexio_invoice_to_woocommerce_document_name', // String,
            self::$prefix . 'attach_bexio_order_to_woocommerce_email_statuses', // [String],
            self::$prefix . 'attach_bexio_order_to_woocommerce_document_name', // String,
            self::$prefix . 'attach_bexio_document_to_woocommerce_email_for_payment_methods', // [String],
            self::$prefix . 'custom_invoice_email_enabled', // Boolean,
            self::$prefix . 'auto_send_invoice_by_email', // Boolean,
            self::$prefix . 'auto_send_invoice_by_email_exclude_payment_methods', // String,
            self::$prefix . 'auto_send_invoice_by_email_to_fixed_addresses', // String,
            self::$prefix . 'invoice_email_default_subject', // String,
            self::$prefix . 'invoice_email_default_message', // String,
            self::$prefix . 'default_contact_if_no_customer', // String,
            self::$prefix . 'bexio_default_tax', // String,
            self::$prefix . 'bexio_default_exempt_tax', // String,
            self::$prefix . 'bexio_default_user', // String,
            self::$prefix . 'bexio_default_document_template', // String,
            self::$prefix . 'bexio_default_product_income_account', // String,
            self::$prefix . 'bexio_default_service_income_account', // String,
            self::$prefix . 'bexio_default_shipping_income_account', // String,
            self::$prefix . 'bexio_default_bank_account', // String,
            self::$prefix . 'bexio_default_contacts_group', // String,
            self::$prefix . 'bexio_default_currency', // String,
            self::$prefix . 'bexio_default_language', // String,
            self::$prefix . 'invoice_auto_match_language', // Boolean,
            self::$prefix . 'custom_payment_methods_to_bank_accounts', // [Array],
            self::$prefix . 'ignore_payment_sync_for_payment_methods', // [Array],
            self::$prefix . 'custom_vat_mapping', // [Array],
            self::$prefix . 'use_bexio_products_taxes', // Boolean,
            self::$prefix . 'use_bexio_products_taxes_shipping_tax', // String,
            self::$prefix . 'newsletter', // Boolean,
            self::$prefix . 'completed_initial_setup_steps', // [String],
            self::$prefix . 'create_one_bexio_delivery_per_shopiy_fulfillment', // Boolean,
            self::$prefix . 'auto_setup_done', // Boolean,

            self::$prefix . 'enable_logging', // Boolean,
        );
        return $options;
    }

    public function is_auto_setup_done()
    {
        // update_option(self::$prefix . 'auto_setup_done', true);
        // update_option(self::$prefix . 'auto_setup_done', false);
        return new \WP_REST_Response([
            "auto_setup_done" => get_option(self::$prefix . 'auto_setup_done')
        ], 200);
    }

    public function get_current_subscriptions() {
        return API::instance()->get_current_subscriptions();
    }

    public function update_current_subscriptions($request) {
        $body = $request->get_params();
        if (isset($body['variantId']) && is_numeric($body['variantId'])) {
            return API::instance()->update_current_subscriptions($body["variantId"]);
        } else {
            return new \WP_REST_Response(["error" => "Missing parameter variantId"], 500);
        }
    }

    public function get_subscription_plans() {
        return API::instance()->get_subscription_plans();
    }

    public function get_checkout_url($request) {
        $params = $request->get_params();

        if (isset($params['variantId']) && is_numeric($params['variantId'])) {
            $variant_id = intval($params['variantId']);
            return API::instance()->get_checkout($variant_id);
        } else {
            return new \WP_REST_Response(["error" => "Missing parameter variantId"], 500);
        }
    }

    public function get_customer_portal_url() {
        return API::instance()->get_customer_portal_url();
    }

    public function get_woocommerce_settings() {
        $payment_gateways = \WC_Payment_Gateways::instance();
        $woocommerce_payment_gateways = $payment_gateways->payment_gateways();

        $payment_methods = [];
        foreach ( $woocommerce_payment_gateways as $gateway ) {
            $title = $gateway->get_title();
            if ($gateway->enabled === 'yes') {
                $title .= " (active)";
            }
            if (strpos($gateway->id, 'pos_') === 0) {
                $title .= " (POS)";
            }

            $payment_methods[] = [
                'id'    => $gateway->id,
                'title' => $title,
                'description' => $gateway->get_description(),
            ];
        }

        $gift_cards_plugin_enabled = class_exists('WC_Gift_Cards');
        if ($gift_cards_plugin_enabled) {
            $payment_methods[] = [
                'id'    => 'gift_card',
                'title' => 'Gift Card',
                'description' => 'Gift Cards Plugin',
            ];
        }

        $admin_email = get_option('admin_email');
        $admin_user = get_user_by('email', $admin_email);
        $first_name = $admin_user ? get_user_meta($admin_user->ID, 'first_name', true) : "";
        $last_name = $admin_user ? get_user_meta($admin_user->ID, 'last_name', true) : "";
        $taxes_enabled = get_option('woocommerce_calc_taxes') === 'yes';
    
        return new \WP_REST_Response([
            "paymentGateways" => $payment_methods,
            "taxesEnabled" => $taxes_enabled,
            "adminUser" => [
                "email" => $admin_email,
                "firstName" => $first_name,
                "lastName" => $last_name,
            ],
        ], 200);
    }

    public function get_bexio_settings() {
        return API::instance()->get_bexio_settings();
    }

    public function perform_auto_setup()
    {
        if (get_option(self::$prefix . 'auto_setup_done')) {
            return new \WP_REST_Response(["message" => "Auto setup already done"], 200);
        }

        $bexioSettings = API::instance()->get_bexio_settings();
        $wc_locale = strtolower(get_locale());
        $wc_currency = "CHF";
        $settings = [];

        // Default currency
        foreach ($bexioSettings['currencies'] as $currency) {
            if ($currency['name'] == $wc_currency) {
                $settings[self::$prefix . 'bexio_default_currency'] = $currency['id'];
                break;
            }
        }
        // Default Tax
        foreach ($bexioSettings['taxes'] as $tax) {
            if ($tax['code'] == 'UN81') {
                $settings[self::$prefix . 'bexio_default_tax'] = $tax['id'];
                break;
            }
        }
        // Default Exempt Tax
        foreach ($bexioSettings['taxes'] as $tax) {
            if ($tax['code'] == 'UEX') {
                $settings[self::$prefix . 'bexio_default_exempt_tax'] = $tax['id'];
                break;
            }
        }
        // Tax Mapping
        try {
            // Keep only active sales taxes from bexio
            $bexioActiveSalesTaxes = array_filter($bexioSettings['taxes'], function ($e) {
                return isset($e['type']) && $e['type'] == "sales_tax" && isset($e['is_active']) && $e['is_active'] && isset($e['value']) && isset($e['id']);
            });
        
            // If some rates are found in double, keep the one with the highest ID (i.e. most recent tax in bexio)
            $acc = [];
            foreach ($bexioActiveSalesTaxes as $obj) {
                if (!isset($acc[$obj['value']]) || $acc[$obj['value']]['id'] < $obj['id']) {
                    $acc[$obj['value']] = $obj;
                }
            }
            $filteredBexioTaxes = array_values($acc);
            $settings[self::$prefix . 'custom_vat_mapping'] = array_map(function ($e) {
                return ['vatRate' => $e['value'], 'bexioTax' => $e['id']];
            }, $filteredBexioTaxes);
        
        } catch (\Exception $e) {
            // In case of failure, keep a simpler way of processing taxes (without de-duplication)
            $settings[self::$prefix . 'custom_vat_mapping'] = array_map(function ($e) {
                return ['vatRate' => $e['value'], 'bexioTax' => $e['id']];
            }, array_filter($bexioSettings['taxes'], function ($e) {
                return isset($e['type']) && $e['type'] == "sales_tax" && isset($e['is_active']) && $e['is_active'] && isset($e['value']) && isset($e['id']);
            }));
        }
        // Default language
        foreach ($bexioSettings['languages'] as $e) {
            if (isset($e['iso_639_1']) && strpos($wc_locale, strtolower($e['iso_639_1'])) === 0) {
                $settings[self::$prefix . 'bexio_default_language'] = $e['id'];
                break;
            }
        }
        // Default user
        foreach ($bexioSettings['users'] as $e) {
            if (isset($e['is_superadmin']) && $e['is_superadmin']) {
                $settings[self::$prefix . 'bexio_default_user'] = $e['id'];
                break;
            }
        }
        if (!$settings[self::$prefix . 'bexio_default_user'] && isset($bexioUsers[0]['id'])) {
            $settings[self::$prefix . 'bexio_default_user'] = $bexioUsers[0]['id'];
        }
        // Default product income account
        foreach ($bexioSettings['incomeAccounts'] as $e) {
            if (isset($e['account_no']) && $e['account_no'] == "3201") {
                $settings[self::$prefix . 'bexio_default_product_income_account'] = $e['id'];
                break;
            }
        }
        // Default service income account
        foreach ($bexioSettings['incomeAccounts'] as $e) {
            if (isset($e['account_no']) && $e['account_no'] == "3401") {
                $settings[self::$prefix . 'bexio_default_service_income_account'] = $e['id'];
                break;
            }
        }
        // Default shipping income account
        foreach ($bexioSettings['incomeAccounts'] as $e) {
            if (isset($e['account_no']) && $e['account_no'] == "3207") {
                $settings[self::$prefix . 'bexio_default_shipping_income_account'] = $e['id'];
                break;
            }
        }
        // Default bank account
        foreach ($bexioSettings['bankAccounts'] as $e) {
            if (isset($e['name']) && strtolower($e['name']) == "woocommerce") {
                $settings[self::$prefix . 'bexio_default_bank_account'] = $e['id'];
                break;
            }
        }
        // Mailing options
        switch (true) {
            case strpos($wc_locale, 'fr') === 0:
                $settings[self::$prefix . 'invoice_email_default_subject'] = "Facture de votre commande {order_name}";
                $settings[self::$prefix . 'invoice_email_default_message'] = "Bonjour,\nVeuillez trouver votre facture ci-joint et au lien suivant: [Network Link]\nCordialement";
                $settings[self::$prefix . 'invoice_shipping_line_text'] = "Livraison ({shipping_line_title})";
                $settings[self::$prefix . 'invoice_discount_line_text'] = "Rabais";
                break;
        
            case strpos($wc_locale, 'de') === 0:
                $settings[self::$prefix . 'invoice_email_default_subject'] = "Rechnung für Ihre Bestellung {order_name}";
                $settings[self::$prefix . 'invoice_email_default_message'] = "Guten Tag,\nIhre Rechnung finden Sie anbei und unter folgendem Link: [Network Link]\nMit freundlichen Grüßen";
                $settings[self::$prefix . 'invoice_shipping_line_text'] = "Versand ({shipping_line_title})";
                $settings[self::$prefix . 'invoice_discount_line_text'] = "Rabatt";
                break;
        
            case strpos($wc_locale, 'it') === 0:
                $settings[self::$prefix . 'invoice_email_default_subject'] = "Fattura per il vostro ordine {order_name}";
                $settings[self::$prefix . 'invoice_email_default_message'] = "Salve,\nIn allegato trovate la vostra fattura e al seguente link: [Network Link]\nCordiali saluti";
                $settings[self::$prefix . 'invoice_shipping_line_text'] = "Spedizione ({shipping_line_title})";
                $settings[self::$prefix . 'invoice_discount_line_text'] = "Sconto";
                break;
        
            default: // en
                $settings[self::$prefix . 'invoice_email_default_subject'] = "Invoice for your order {order_name}";
                $settings[self::$prefix . 'invoice_email_default_message'] = "Hello,\nPlease find your invoice attached and at the following link: [Network Link]\nKind regards";
                $settings[self::$prefix . 'invoice_shipping_line_text'] = "Shipping ({shipping_line_title})";
                $settings[self::$prefix . 'invoice_discount_line_text'] = "Discount";
                break;
        }

        $settings[self::$prefix . 'product_identifier_field'] = "sku"; // No variant for now
        $settings[self::$prefix . 'invoice_title_format'] = "WooCommerce #{woocommerce_order_number}";
        $settings[self::$prefix . 'invoice_line_item_format'] = "{woocommerce_product_name}";
        $settings[self::$prefix . 'invoice_line_add_sh_code'] = false;
        $settings[self::$prefix . 'invoice_header_text'] = "";
        $settings[self::$prefix . 'invoice_payment_header_text'] = "";
        $settings[self::$prefix . 'invoice_footer_text'] = "";
        $settings[self::$prefix . 'sync_products'] = 0;
        $settings[self::$prefix . 'sync_products_description'] = 0;
        $settings[self::$prefix . 'sync_orders'] = 0;
        $settings[self::$prefix . 'sync_payments'] = 1;
        $settings[self::$prefix . 'auto_send_invoice_by_email'] = 0;
        $settings[self::$prefix . 'bexio_default_contacts_group'] = null;
        $settings[self::$prefix . 'newsletter'] = true;
        $settings[self::$prefix . 'invoice_auto_match_language'] = 0;
        $settings[self::$prefix . 'auto_setup_done'] = 1;

        // Default sync settings
        $settings[self::$prefix . 'sync_orders_statuses'] = ["pending", "on-hold", "processing", "completed"];
        $settings[self::$prefix . 'auto_create_delivery_notes'] = true;
        $settings[self::$prefix . 'sync_invoices_statuses'] = ["pending", "on-hold", "processing", "completed"];
        $settings[self::$prefix . 'sync_issue_invoices_statuses'] = ["pending", "on-hold", "processing", "completed"];
        $settings[self::$prefix . 'sync_fulfillments_statuses'] = ["completed"];

        $this->batch_upsert_options($settings);
        return new \WP_REST_Response($settings, 200);
    }

    public function get_settings()
    {
        $result = [];
        foreach ($this->registered_settings() as $key) {
            if ($value = get_option($key)) {
                $result[$key] = $value;
            }
        }

        _bcbh_log("SETTINGS: " . json_encode($result));

        return new \WP_REST_Response($result, 200);
    }

    public function set_settings($data)
    {
        $options = $data->get_params();
        $this->batch_upsert_options($options);

        return new \WP_REST_Response($data, 200);
    }

    private function batch_upsert_options($options) {
        $registered_settings = $this->registered_settings();
        $allowed_html = $this->allowed_html_settings();

        foreach ($options as $key => $value) {
            if (in_array($key, $registered_settings)) {
                $sanitized_data = in_array($key, $allowed_html) ? wp_kses_post($value) : $value;
                if (false === get_option($key)) {
                    add_option($key, $sanitized_data);
                } else {
                    update_option($key, $sanitized_data);
                }
            }
        }
    }

    /**
     * Permission Callback
     **/
    public function get_permission()
    {
        if (current_user_can('administrator') || current_user_can('manage_woocommerce')) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Cloning is forbidden.
     */
    public function __clone()
    {
        _doing_it_wrong(__FUNCTION__, __('Cheatin&#8217; huh?'), BEXIO_CONNECTOR_BY_HELVY_VERSION);
    }
}
