<?php

namespace CouponsPlus\App\Data\Export;

use WC_Coupon;
use Throwable;
use WC_Data_Store;
use WC_Shipping_Zone;
use WC_Shipping_Zones;
use WC_Payment_Gateway;
use TGM_Plugin_Activation;
use CouponsPlus\App\Coupon\Rows;
use CouponsPlus\App\Export\DataExporter;
use CouponsPlus\Original\Environment\Env;
use CouponsPlus\App\Coupon\Meta\ColumnMeta;
use CouponsPlus\App\Coupon\Tree\Data\AppData;
use CouponsPlus\App\Offers\OffersRegistrator;
use CouponsPlus\App\Coupon\ColumnsRegistrator;
use CouponsPlus\App\Handlers\IDSLoaderHandler;
use CouponsPlus\App\Coupon\Columns\SimpleColumn;
use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\App\Conditions\FiltersRegistrator;
use CouponsPlus\App\Coupon\Tree\Data\AppDataLoader;
use CouponsPlus\App\Data\Products\ProductIdsLoader;
use CouponsPlus\App\Conditions\ConditionsRegistrator;
use CouponsPlus\App\Coupon\Tree\Data\Abilities\PreloadableData;
use CouponsPlus\App\Coupon\Columns\OfferColumns\SimpleOfferColumn;
use CouponsPlus\App\Conditions\BuiltIn\Conditions\Time\UnitsOfTime;
use CouponsPlus\App\Coupon\Tree\Abilities\OffersItemsSetResolvable;

#[\AllowDynamicProperties]
Class DashboardData
{
    public function export() : Collection
    {
        global $post, $wpdb;

        $this->wpdb = $wpdb;
        (object) $mapTerms = function(Collection $terms) {
            return $terms->map(function(\WP_Term $term) : array {
                    return [
                        'id' => (integer) $term->term_id,
                        'name' => esc_html($term->name)
                    ];
                }
            );
        };
        // load app data (tree) from source
        $treeAppData = $this->loadTreeAppData($post->ID);

        $isLegacy = apply_filters(Env::getWithPrefix('is_legacy_installation'), false);

        (object) $data = (new Collection([
            'dashboardPreferences' => [
                'shouldPreload' => [
                    'categories' => $shouldPreloadCategories = $isLegacy,
                    'tags' => $shouldPreloadTags = $isLegacy
                ],
                'permissions' => [
                    'connect' => get_option(Env::getWithPrefix('permissions.connect'), false)
                ]
            ],
            'urls' => [
                'store' => esc_url(get_permalink( wc_get_page_id( 'shop' ) )),
                'adminAPI' => esc_url(admin_url('admin-post.php')),
                'adminAJAX' => esc_url(admin_url('admin-ajax.php')),
                'rest' => [
                    'WooCommerce' => rest_url( '/wc/v3/' ),
                    'couponsPlus' => rest_url( '/coupons-plus/v1/' ),
                    'products' => [
                        'categories' => rest_url( '/wc/v3/products/categories' ),
                        'tags' => rest_url( '/wc/v3/products/tags' ),
                    ],
                    'places' => [
                        'countries' => rest_url( '/coupons-plus/v1/countries' ),
                        'states' => rest_url( '/coupons-plus/v1/states' ),
                    ],
                    'preferences' => rest_url( '/coupons-plus/v1/preferences' ),
                    'users' => [
                        'search' => rest_url('/coupons-plus/v1/users/search'),
                    ],
                    'presets' => [
                        'steps' => rest_url( '/coupons-plus/v1/presets/steps' ),
                        'byId' => rest_url( '/coupons-plus/v1/presets/' ),
                    ],
                ],
                'storage' => esc_url(Env::directoryURI().'storage'),
                'icons' => [
                    'noRows' => esc_url(Env::directoryURI().'storage/icons/welcome.svg') 
                ],
            ],
            'security' => [
                'nonces' => [
                    'search' => esc_html(wp_create_nonce('search-products')),
                    'dashboard' => esc_html(wp_create_nonce('coupons-plus-dashboard')),
                    'rest' => wp_create_nonce( 'wp_rest' ),
                ]
            ],
            'textDomain' => esc_html(Env::settings()->app->textDomain),
            'state' => [
                'version' => 1,
                'grove' => $treeAppData->grove->getData()->asArray(),
                'preconditions' => $treeAppData->preconditions->getData(),
                'predates' => $treeAppData->predates->getData(),
                'preloadedData' => $this->mapPreloadedData($treeAppData),
                'autoApply' => $treeAppData->autoApply? 'yes': 'no',
                'model' => $treeAppData->model,
                'code' => $post->post_title,
            ],
            'options' => [
                /**
                 * Escaped indivudally within JSONMapper
                 */
                'rows' => json_decode(
                    (new DataExporter)->export(
                        Rows::createFromOptions(
                            get_post_meta(
                                $post->ID, 
                                Env::getWithPrefix('rows'), 
                                $single = true
                            ) ?: '{}', 
                            new \WC_Coupon
                        )
                    )
                )->rows,
                'preconditionsColumn' => json_decode(
                    get_post_meta(
                        $post->ID, 
                        Env::getWithPrefix('preconditions'), 
                        $single = true
                    ) ?: '[]'
                ) ?: [
                    "temporaryID" => 'preconditions-column',
                    "type" => SimpleColumn::TYPE,
                    "testableType" => "conditions",
                    "contexts" => [
                        [
                            'temporaryID' => 'preconditions-context',
                            'conditionsOrFilters' => [],
                            "offers" => []
                        ],
                    ]
                ],
                // boolean, no need to be escaped.
                'coupon_auto_apply_is_enabled' => (get_post_meta(
                    $post->ID, 
                    Env::getWithPrefix('coupon_auto_apply_is_enabled'), 
                    $single = true
                ) === 'yes')
            ],
            'components' => [
                //
                // todo: remove duplications
                //
                'conditions' => ConditionsRegistrator::get()->all()->map(function(string $Condition) : array {
                    return [
                        'type' => esc_html($Condition::TYPE),
                        'name' => esc_html($Condition::getName()),
                        'description' => esc_html($Condition::getDescription()),
                        'useCases' => $Condition::getUseCases(),
                        'notes' => $Condition::getNotes(),
                        // escaped individually
                        'fields' => $Condition::getOptions(),
                        // escaped individually
                        'defaultOptions' => $Condition::exportDefault()
                    ];
                }),
                'filters' => FiltersRegistrator::get()->all()->map(function(string $Filter) : array {
                    return [
                        'type' => esc_html($Filter::TYPE),
                        'name' => esc_html($Filter::getName()),
                        'description' => esc_html($Filter::getDescription()),
                        'useCases' => $Filter::getUseCases(),
                        // escaped individually
                        'fields' => $Filter::getOptions(),
                        // escaped individually
                        'defaultOptions' => $Filter::exportDefault(),
                    ];
                }),
                'offers' => OffersRegistrator::get()->all()->map(function(string $Offer) : array {
                    return [
                        'type' => esc_html($Offer::TYPE),
                        'name' => esc_html($Offer::getName()),
                        'description' => esc_html($Offer::getDescription()),
                        'useCases' => $Offer::getUseCases(),
                        'notes' => $Offer::getNotes(),
                        // escaped individually
                        'fields' => $Offer::getAllOptionsMap(),
                        // escaped individually
                        'defaultOptions' => $Offer::exportDefault(),
                        'iconURL' => esc_url($Offer::getIconUrl()),
                        'supports' => [
                            'recursiveness' => is_a($Offer, OffersItemsSetResolvable::class, $allowString = true)
                        ]
                    ];
                }),
                'columns' => ColumnsRegistrator::get()->all()->map(function(string $Column) : array {
                    (object) $columnMeta = $Column::getColumnMeta();

                    return [
                        'type' => esc_html($Column::TYPE),
                        'meta' => [
                            'name' => esc_html($columnMeta->getName()),
                            'description' => esc_html($columnMeta->getDescription()),
                            // boolean, data type doesn't need to be escaped
                            'isOffersColumn' => $columnMeta->isOffersColumn(),
                            'useOneOffersSetForAllContexts' => $columnMeta->useOneOffersSetForAllContexts(),
                            'preferredColumnConversion' => esc_html($columnMeta->getPreferredColumnConversion()),
                        ]
                    ];
                }),
                'defaultColumnOptions' => ColumnMeta::getDefaultOptions()->asArray(),
                'defaultColumnTypes' => [
                    'regular' => SimpleColumn::TYPE,
                    'offers' => SimpleOfferColumn::TYPE
                ],
                'rows' => [
                    'types' => [
                        'main' => 'main',
                        'preconditions' => 'preconditions',
                    ]
                ]
            ],
            'stubs' => [
                'conditions' => ConditionsRegistrator::get()->stubs()->map(function(string $Filter) : array {
                    return [
                        'type' => esc_html($Filter::TYPE),
                        'name' => esc_html($Filter::getName()),
                        'description' => esc_html($Filter::getDescription()),
                        'useCases' => $Filter::getUseCases(),
                    ];
                }),
                'filters' => FiltersRegistrator::get()->stubs()->map(function(string $Filter) : array {
                    return [
                        'type' => esc_html($Filter::TYPE),
                        'name' => esc_html($Filter::getName()),
                        'description' => esc_html($Filter::getDescription()),
                        'useCases' => $Filter::getUseCases(),
                    ];
                }),
                'offers' => OffersRegistrator::get()->nonImplementedStubs()->map(function(string $Filter) : array {
                    return [
                        'type' => esc_html($Filter::TYPE),
                        'name' => esc_html($Filter::getName()),
                        'description' => esc_html($Filter::getDescription()),
                        'useCases' => $Filter::getUseCases(),
                    ];
                }),
            ],
            'presets' => require Env::appDirectory('presets/').'builtin.php',
            'WooCommerce' => $WooCommerceData = [
                'currency' => esc_html(get_woocommerce_currency()),
                'currencyPosition' => get_option( 'woocommerce_currency_pos' )
            ],
            'woocommerce' => $WooCommerceData, // backwards compatibility
            'userRoles' => Collection::create(get_editable_roles())->map(function(array $meta) : string {
                return esc_html($meta['name'] ?? '');
            }),
            'paymentMethods' => Collection::create(WC()->payment_gateways->get_available_payment_gateways())->map(function(WC_Payment_Gateway $gateway) : string {
                return $gateway->title ?? '';
            }),
            'shippingMethods' => $this->getShippingMethods(),
            'shippingZones' => Collection::create(WC_Data_Store::load('shipping-zone')->get_zones())->mapWithKeys(function($rawZone) : array {
                (object) $zone = new WC_Shipping_Zone($rawZone);

                return [
                    'key' => (integer) $zone->get_id(),
                    'value' => $zone->get_zone_name($edit = 'view')
                ];
            }),
            'successfulOrderStatuses' => Collection::create(wc_get_is_paid_statuses())->mapWithKeys(function(string $status) : array {
                (array) $allStatuses = wc_get_order_statuses();
                (string) $statusKey = 'wc-' . $status;
                
                return [
                    'key' => esc_html($status),
                    'value' => isset($allStatuses[$statusKey]) ? esc_html($allStatuses[$statusKey]) : esc_html($status)
                ];
            }),
            'time' => (new UnitsOfTime)->getAll()->asArray(),
            'cache' => [
                // an array of object options
            ],
            'attributes' => Collection::create(wc_get_attribute_taxonomies() ?: [])->map(function($attribute) : array {
                return [
                    'id' => (integer) $attribute->attribute_id,
                    'name' => esc_html($attribute->attribute_label),
                    'slug' => esc_html($attribute->attribute_name),
                    'type' => esc_html($attribute->attribute_type),
                    'isVariation' => (boolean)isset($attribute->is_variation)? $attribute->is_variation : false,
                    'values' => Collection::create(get_terms([
                        'taxonomy' => 'pa_' . sanitize_title($attribute->attribute_name),
                        'hide_empty' => false,
                    ]) ?: [])->map(function($term) {
                        return [
                            'id' => (integer)$term->term_id,
                            'name' => esc_html($term->name),
                            'slug' => esc_html($term->slug)
                        ];
                    })->getValues()->asArray(),
                ];
            })->getValues()->asArray(),
            // this needs to be called AFTER the rows have been exported
            // so that the ids are properly registered by ProductIdsLoader
            'categories' => $mapTerms(
                $shouldPreloadCategories? 
                    Collection::create(get_terms([
                        'taxonomy' => 'product_cat',
                        'hide_empty' => false,
                    ])) 
                : 
                    /*here are the categories from the ids that have been saved in the db only neede here because we're using AJAX*/
                    Collection::create(get_terms([
                        'taxonomy' => 'product_cat',
                        'hide_empty' => false,
                        'include' => Collection::create(IDSLoaderHandler::instance()->productCategories)->asArray()
                    ]))
            ),
            'tags' => $mapTerms(
                $shouldPreloadTags? 
                    Collection::create(get_terms([
                        'taxonomy' => 'product_tag',
                        'hide_empty' => false,
                    ])) 
                : 
                    /*here are the categories from the ids that have been saved in the db only neede here because we're using AJAX*/
                    Collection::create(get_terms([
                        'taxonomy' => 'product_tag',
                        'hide_empty' => false,
                        'include' => Collection::create(IDSLoaderHandler::instance()->productTags)->asArray()
                    ]))
            ),
            'products' => [
                'idsWithVariations' => (object) [],
                'labels' => (object) [],
                
                'default' => [
                    // this needs to be called AFTER the rows have been exported
                    // so that the ids are properly registered by ProductIdsLoader
                    'idsWithVariations' => ProductIdsLoader::instance()->getProductIds()
                                                                       ->asArray(),
                                            /*
                                                // refer to CouponsPlus\App\Conditions\BuiltIn\Filters\Products
                                                // for the format used here
                                            */
                    'labels' => ProductIdsLoader::instance()->getLabels()
                                                            ->asArray()
                ]
            ],
            'text' => [
                'add' => [
                    'conditions' => __('Add condition', 'coupons-plus-for-woocommerce'),
                    'filters' => __('Add filter', 'coupons-plus-for-woocommerce'),
                    'conditionOrFilter' => __('Add condition or filter', 'coupons-plus-for-woocommerce'),
                    'offers' => __('Add offer', 'coupons-plus-for-woocommerce'),
                    'rows' => __('Add row', 'coupons-plus-for-woocommerce'),
                    'context' => __('Add context (group)', 'coupons-plus-for-woocommerce')
                ],
                'create' => [
                    'column' => __('Create Column', 'coupons-plus-for-woocommerce')
                ]
            ],
            'databaseDateFormat' => [
                'full' => 'Y-m-d H:i:s',
                'withoutTime' => 'Y-m-d',
            ],
            'compatibility' => [
                'legacy' => [
                    'isLegacyInstallation' => $isLegacy
                ],
                'couponURLs' => [
                    'isAvailable' => class_exists(\CouponURLs\Original\Core\Application::class),
                    'installationURL' => class_exists(TGM_Plugin_Activation::class)? TGM_Plugin_Activation::get_instance()->get_tgmpa_url(): '#'
                ]
            ],
            'changelog' => (function() : string {
                ob_start();

                include Env::directory().'/change.log';

                return ob_get_clean();
            })(),
            'branding' => [
                'logo' => [
                    'dark' => Env::directoryURI().'/storage/branding/logo-dark.png'
                ]
            ],
            'implementations' => [
                'offers' => [
                    'GetY' => [
                        'advanced' => apply_filters(Env::getWithPrefix('GetY.advanced.isImplemented'), false)
                    ],
                    'Discount' => [
                        'extra' => apply_filters(Env::getWithPrefix('Discount.extra.isImplemented'), false)
                    ],
                ],
                'branches' => [
                    'tiered' => apply_filters(Env::getWithPrefix('Branch.tiered.isImplemented'), false),
                ],
                'engine' => [
                    'trees' => apply_filters(Env::getWithPrefix('Engine.Trees.isImplemented'), false),
                    'testableComposite' => apply_filters(Env::getWithPrefix('Engine.TestableComposite.isImplemented'), false),
                ],
                'Coupons+ Pro' => class_exists(\CouponsPlusPro\Original\Environment\Env::class),
            ],
            'environment' => Env::settings()->environment
        ]))->merge(apply_filters(Env::getWithPrefix('dashboard_data_extra_export_data'), new Collection([])));

        return $data;
    }
 
    private function loadTreeAppData(int $couponID) : AppData
    {
        // load tree app data from source and mapped
        return (new AppDataLoader(new WC_Coupon($couponID)))->load();
    }

    /**
     * @param AppData $treeAppData
     * @return Collection
     */
    protected function mapPreloadedData(AppData $treeAppData): Collection
    {
        return $treeAppData->preloadedData->getData()->map(function (object $item) {
            $allComponents = new Collection([]);
            $allComponents = $allComponents->concat(FiltersRegistrator::get()->all()->asArray());
            $allComponents = $allComponents->concat(ConditionsRegistrator::get()->all()->asArray());
            $allComponents = $allComponents->concat(OffersRegistrator::get()->all()->asArray());

            $preloader = $allComponents->find(function (string $Component) use ($item): bool {
                if (!is_a($Component, PreloadableData::class, $allowString = true)) {
                    return false;
                }

                return $item->category === $Component::preloadedData()->get('category');
            });

            if ($preloader) {
                $item->values = $preloader::preloadedData()->get('data')($item->values);
            }

            return $item;
        });
    }

    protected function getShippingMethods(): array
    {
        $all_method_instances = [];
        $raw_shipping_zones = [];

        // --- Step 1: Get the raw data from WooCommerce ---
        try {
            // This is returning an array of arrays on your site.
            $raw_shipping_zones = WC_Shipping_Zones::get_zones();
        } catch (Throwable $e) {
            // Log error if needed, continue with an empty array.
        }

        // --- Step 2: Add the "Rest of the World" zone, which IS an object ---
        $rest_of_world_zone = WC_Shipping_Zones::get_zone(0);
        if ($rest_of_world_zone) {
            // Add it to our raw list. Now we have a mixed array.
            $raw_shipping_zones[0] = $rest_of_world_zone;
        }

        // --- Step 3: Loop through the mixed array and handle each type correctly ---
        foreach ($raw_shipping_zones as $zone_data) {
            $shipping_methods_in_zone = [];
            $zone_name = 'Rest of the World'; // Default name

            // Check if the current item is an OBJECT (like the "Rest of the World" zone)
            if (is_object($zone_data) && $zone_data instanceof WC_Shipping_Zone) {

                $shipping_methods_in_zone = $zone_data->get_shipping_methods(true);
                $zone_name = $zone_data->get_zone_name();

                // Check if the current item is an ARRAY (like your "Americaaa" zone)
            } elseif (is_array($zone_data)) {

                // Safely get the methods and name from the array keys
                if (isset($zone_data['shipping_methods']) && is_array($zone_data['shipping_methods'])) {
                    $shipping_methods_in_zone = $zone_data['shipping_methods'];
                }
                if (isset($zone_data['zone_name'])) {
                    $zone_name = $zone_data['zone_name'];
                }
            } else {
                // If it's neither, skip this iteration.
                continue;
            }

            // --- Step 4: Process the methods we found ---
            foreach ($shipping_methods_in_zone as $method) {
                // Final safety check to ensure we have a method object
                if (!is_object($method) || !method_exists($method, 'get_rate_id')) {
                    continue;
                }

                $unique_key = $method->get_rate_id(); // e.g., 'flat_rate:3'
                $method_title = $method->get_title(); // e.g., 'Federal Express'

                $display_value = sprintf('%s (%s)', $method_title, $zone_name);

                $all_method_instances[$unique_key] = $display_value;
            }
        }

        return $all_method_instances;
    }
}
