<?php

namespace Opencart\Admin\Controller\Extension\Alsendo\Module;

require_once(DIR_EXTENSION . 'alsendo/src/Database/DatabaseMigrator.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/CoreConfigurationRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/SaveRegionRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/SaveApiKeysRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/SaveSenderTemplateRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/UpdateSenderTemplateRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/DeleteTemplateRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/SetTemplateAsMainRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/GetTemplateDataRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/SaveShippingSettingsTemplateRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/UpdateShippingSettingsTemplateRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/ConfigurationRequests/SaveAdditionalSettingsRequestDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/Unified/SimpleSubmittedOrderDetailsDTO.php');
require_once(DIR_EXTENSION . 'alsendo/src/DTO/Entity/AlsendoRegionEntity.php');
require_once(DIR_EXTENSION . 'alsendo/src/Models/Config.php');
require_once(DIR_EXTENSION . 'alsendo/src/Models/WrapperResult.php');
require_once(DIR_EXTENSION . 'alsendo/src/Models/SenderAddressTemplate.php');
require_once(DIR_EXTENSION . 'alsendo/src/Models/ShippingSettingsTemplate.php');
require_once(DIR_EXTENSION . 'alsendo/src/Models/AdditionalSettings.php');
require_once(DIR_EXTENSION . 'alsendo/src/Models/Service.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/ConfigRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/SenderAddressRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/ShippingSettingsRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/AdditionalSettingsRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/ServicesRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/OrderShipmentRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/OrderShipmentPackageRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/OrderPickupPointRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Repositories/ShippingMethodRepository.php');
require_once(DIR_EXTENSION . 'alsendo/src/Services/WrapperService.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/ConfigHandler.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/SenderAddressHandler.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/ShippingSettingsHandler.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/UpdateOrderDetailsFromOpenCartOrder.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/AdditionalSettingsHandler.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/OrderUpdateTemplates.php');
require_once(DIR_EXTENSION . 'alsendo/src/Handlers/Region/RegionHandler.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/CommonDataProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/SenderAddressDataProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/ShippingSettingsDataProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/ApaczkaPackageTypesProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/AdditionalSettingsDataProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/PickupLabelProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/OrderPickupPointProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/DefaultCarrierPickupPointsProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Services/MapWidgetService.php');
require_once(DIR_EXTENSION . 'alsendo/src/Providers/OrderShipmentProvider.php');
require_once(DIR_EXTENSION . 'alsendo/src/Services/PickupTypeService.php');
require_once(DIR_EXTENSION . 'alsendo/src/Services/PackageContentTags.php');
require_once(DIR_EXTENSION . 'alsendo/src/Enums/OrderShipmentStatus.php');
require_once(DIR_EXTENSION . 'alsendo/src/Helpers/Country.php');
require_once(DIR_EXTENSION . 'alsendo/src/OrderDetails/CreateOrderDetailsFactory.php');

use Opencart\Admin\Model\Extension\Alsendo\Module\AlsendoOrderDetails;
use Opencart\Extension\Alsendo\Database\DatabaseMigrator;
use Opencart\Extension\Alsendo\DTO\Entity\AlsendoRegionEntity;
use Opencart\Extension\Alsendo\DTO\Entity\OrderDetailsEntity;
use Opencart\Extension\Alsendo\DTO\Unified\SimpleSubmittedOrderDetailsDTO;
use Opencart\Extension\Alsendo\Enums\OrderShipmentStatus;
use Opencart\Extension\Alsendo\Handlers\AdditionalSettingsHandler;
use Opencart\Extension\Alsendo\Handlers\ConfigHandler;
use Opencart\Extension\Alsendo\Handlers\OrderUpdateTemplates;
use Opencart\Extension\Alsendo\Handlers\Region\RegionHandler;
use Opencart\Extension\Alsendo\Handlers\SenderAddressHandler;
use Opencart\Extension\Alsendo\Handlers\ShippingSettingsHandler;
use Opencart\Extension\Alsendo\Handlers\UpdateOrderDetailsFromOpenCartOrder;
use Opencart\Extension\Alsendo\Helpers\Country;
use Opencart\Extension\Alsendo\OrderDetails\CreateOrderDetailsFactory;
use Opencart\Extension\Alsendo\Providers\ApaczkaPackageTypesProvider;
use Opencart\Extension\Alsendo\Providers\AdditionalSettingsDataProvider;
use Opencart\Extension\Alsendo\Providers\CommonDataProvider;
use Opencart\Extension\Alsendo\Providers\DefaultCarrierPickupPointsProvider;
use Opencart\Extension\Alsendo\Providers\OrderPickupPointProvider;
use Opencart\Extension\Alsendo\Providers\PickupLabelProvider;
use Opencart\Extension\Alsendo\Repositories\OrderPickupPointRepository;
use Opencart\Extension\Alsendo\Services\MapWidgetService;
use Opencart\Extension\Alsendo\Providers\OrderShipmentProvider;
use Opencart\Extension\Alsendo\Providers\SenderAddressDataProvider;
use Opencart\Extension\Alsendo\Providers\ShippingSettingsDataProvider;
use Opencart\Extension\Alsendo\Repositories\AdditionalSettingsRepository;
use Opencart\Extension\Alsendo\Repositories\ConfigRepository;
use Opencart\Extension\Alsendo\Repositories\OrderShipmentPackageRepository;
use Opencart\Extension\Alsendo\Repositories\OrderShipmentRepository;
use Opencart\Extension\Alsendo\Repositories\SenderAddressRepository;
use Opencart\Extension\Alsendo\Repositories\ServicesRepository;
use Opencart\Extension\Alsendo\Repositories\ShippingMethodRepository;
use Opencart\Extension\Alsendo\Repositories\ShippingSettingsRepository;
use Opencart\Extension\Alsendo\Services\PackageContentTags;
use Opencart\Extension\Alsendo\Services\PickupTypeService;
use Opencart\Extension\Alsendo\Services\WrapperService;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\CoreConfigurationRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\SaveRegionRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\SaveApiKeysRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\SaveSenderTemplateRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\UpdateSenderTemplateRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\DeleteTemplateRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\SetTemplateAsMainRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\GetTemplateDataRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\SaveShippingSettingsTemplateRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\UpdateShippingSettingsTemplateRequestDTO;
use Opencart\Extension\Alsendo\DTO\ConfigurationRequests\SaveAdditionalSettingsRequestDTO;
use Opencart\System\Engine\Controller;

class Alsendo extends Controller {
    private DatabaseMigrator $databaseMigrator;
    private ConfigRepository $configRepository;
    private SenderAddressRepository $senderAddressRepository;
    private ShippingSettingsRepository $shippingSettingsRepository;
    private AdditionalSettingsRepository $additionalSettingsRepository;
    private ServicesRepository $servicesRepository;
    private WrapperService $wrapperService;
    private ConfigHandler $configHandler;
    private SenderAddressHandler $senderAddressHandler;
    private ShippingSettingsHandler $shippingSettingsHandler;
    private AdditionalSettingsHandler $additionalSettingsHandler;
    private RegionHandler $regionHandler;
    private CommonDataProvider $commonDataProvider;
    private DefaultCarrierPickupPointsProvider $defaultCarrierPickupPointsProvider;
    private SenderAddressDataProvider $senderAddressDataProvider;
    private ShippingSettingsDataProvider $shippingSettingsDataProvider;
    private AdditionalSettingsDataProvider $additionalSettingsDataProvider;
    private MapWidgetService $mapWidgetService;
    private ShippingMethodRepository $shippingMethodRepository;
    private OrderShipmentProvider $orderShipmentProvider;
    private PackageContentTags $packageContentTags;
    private AlsendoRegionEntity $currentRegion;
    private OrderPickupPointRepository $orderPickupPointRepository;
    private OrderPickupPointProvider $orderPickupPointProvider;
    private PickupLabelProvider $pickupLabelProvider;
    private ApaczkaPackageTypesProvider $apaczkaPackageTypesProvider;

    public function __construct($registry)
    {
        parent::__construct($registry);

        $this->load->language('extension/alsendo/validation');

        $this->databaseMigrator = new DatabaseMigrator($registry, $this->db,\DB_PREFIX);

        if (!$this->databaseMigrator->isInstalled()) {
            return;
        }

        $this->configRepository = new ConfigRepository($this->db, \DB_PREFIX);
        $this->senderAddressRepository = new SenderAddressRepository($this->db, \DB_PREFIX);
        $this->shippingSettingsRepository = new ShippingSettingsRepository($this->db, \DB_PREFIX);

        $this->additionalSettingsRepository = new AdditionalSettingsRepository($this->db, \DB_PREFIX);
        $this->additionalSettingsDataProvider = new AdditionalSettingsDataProvider($this->additionalSettingsRepository);

        $this->servicesRepository = new ServicesRepository($this->db, \DB_PREFIX);
        $this->shippingMethodRepository = new ShippingMethodRepository($registry, $this->db, \DB_PREFIX);
        $this->orderPickupPointRepository = new OrderPickupPointRepository($this->db, \DB_PREFIX);

        $this->wrapperService = new WrapperService(
            $registry,
            DIR_EXTENSION . 'alsendo/',
            $this->servicesRepository,
            $this->additionalSettingsDataProvider,
            $this->senderAddressRepository
        );
        $this->configHandler = new ConfigHandler($this->configRepository);

        $this->senderAddressHandler = new SenderAddressHandler($this->senderAddressRepository, $this->language);
        $this->shippingSettingsHandler = new ShippingSettingsHandler($this->shippingSettingsRepository, $this->language);
        $this->additionalSettingsHandler = new AdditionalSettingsHandler($this->additionalSettingsRepository);
        $this->regionHandler = new RegionHandler($registry, $this->configRepository);
        $this->commonDataProvider = new CommonDataProvider($this->language, $this->url, $this->session);
        $this->defaultCarrierPickupPointsProvider = new DefaultCarrierPickupPointsProvider();

        $this->senderAddressDataProvider = new SenderAddressDataProvider($this->senderAddressRepository);
        $this->shippingSettingsDataProvider = new ShippingSettingsDataProvider($this->shippingSettingsRepository);
        $this->orderPickupPointProvider = new OrderPickupPointProvider($this->orderPickupPointRepository);

        $orderShipmentRepository =  new OrderShipmentRepository($this->db, \DB_PREFIX);
        $orderShipmentPackageRepository = new OrderShipmentPackageRepository($this->db, \DB_PREFIX);
        $this->orderShipmentProvider = new OrderShipmentProvider($orderShipmentRepository, $orderShipmentPackageRepository);
        $this->packageContentTags = new PackageContentTags($registry);

        $this->load->model('extension/alsendo/module/alsendo_region');

        $configRegionValue = $this->configRepository->getSetting('region') ?? null;
        if ($configRegionValue) {
            $this->model_extension_alsendo_module_alsendo_region->setRegionByCode($this->configRepository->getSetting('region'));
        }

        /** @var null|AlsendoRegionEntity $currentRegion */
        $currentRegion = $this->model_extension_alsendo_module_alsendo_region->getCurrentRegion();

        if (!$currentRegion) {
            $regionFromConfig = $this->configRepository->getSetting('region') ?? 'poland';
            $this->regionHandler->syncRegion($regionFromConfig);

            $currentRegion = $this->model_extension_alsendo_module_alsendo_region->getRegionByCode($regionFromConfig);
        }

        $this->currentRegion = $currentRegion;

        if ($currentRegion) {
            $this->configHandler->saveRegion($currentRegion->code);
        }

        $this->mapWidgetService = new MapWidgetService($this->currentRegion->code);
        $this->pickupLabelProvider = new PickupLabelProvider($this->language, $this->currentRegion->code);
        $this->apaczkaPackageTypesProvider = new ApaczkaPackageTypesProvider();
    }

    public function install(): void
    {
        $this->databaseMigrator->up();

        $this->load->model('setting/module');

        $existingModules = $this->model_setting_module->getModulesByCode('alsendo.alsendo');
        if (empty($existingModules)) {
            $this->model_setting_module->addModule('alsendo.alsendo', [
                'name' => 'Alsendo',
                'status' => '1',
            ]);
        }
    }

    public function uninstall(): void
    {
        $this->databaseMigrator->down();

        $this->load->model('setting/module');
        $this->model_setting_module->deleteModulesByCode('alsendo.alsendo');
    }

    public function index(): void
    {
        $request = CoreConfigurationRequestDTO::createFromPayload($this->request->post ?? []);

        $this->load->language('extension/alsendo/main');
        $this->document->setTitle($this->language->get('text_configuration_page_title'));

        $mapAssets = $this->mapWidgetService->getAssetUrls();
        $this->document->addScript($mapAssets['js']);
        $this->document->addStyle($mapAssets['css']);
        $this->document->addScript('/extension/alsendo/admin/view/javascript/alsendo-map.js');
        $this->document->addScript('/extension/alsendo/admin/view/javascript/combobox.js');
        $this->document->addStyle('/extension/alsendo/admin/view/css/style.css');


        if (!$this->user->hasPermission('access', 'extension/alsendo/module/alsendo')) {
            $this->response->redirect($this->url->link('error/permission', 'user_token=' . $this->session->data['user_token']));
        }

        if (!empty($request->action)) {
            $this->handleAjaxActions($request);
            return;
        }

        $config = $this->configRepository->getConfig();
        $data = $this->commonDataProvider->getData($config);
        $data['api_connection_check_url'] = $this->url->link(
            'extension/alsendo/module/alsendo.apiConnectionCheck',
            'user_token=' . $this->session->data['user_token'],
            true
        );

        $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'];
        $baseUrl = $scheme . '://' . $host;
        $data['ecolet_register_link'] = $baseUrl . '/index.php?route=extension/alsendo/ecolet_auth';
        $data['current_store_currency'] = $this->config->get('config_currency');
        $data = array_merge($data, $this->senderAddressDataProvider->getData());
        $data = array_merge($data, $this->shippingSettingsDataProvider->getData());
        $data['address_book_list'] = [];
        $data['sender_address_external_id_preselect'] = null;
        if ($this->currentRegion->code === 'czechia' && $config->isComplete()) {
            $addressBookResult = $this->wrapperService->getAddressBookList($config);

            if ($addressBookResult->isSuccess() && !empty($addressBookResult->getData()['addresses'])) {
                $data['address_book_list'] = $addressBookResult->getData()['addresses'];
            }
            $mainTemplate = $this->senderAddressRepository->getMainTemplate();
            if ($mainTemplate && $mainTemplate->getExternalId()) {
                $data['sender_address_external_id_preselect'] = $mainTemplate->getExternalId();
            }
        }
        $data = array_merge($data, $this->additionalSettingsDataProvider->getData());
        $additionalSettings = $data['additional_settings'] ?? [];

        $carrierDefinitions = $this->defaultCarrierPickupPointsProvider->getDefinitions($this->currentRegion->code);

        $data['carriers'] = [];
        foreach ($carrierDefinitions as $def) {
            $defaultKey = $def['key'] . '_default_point';
            $data['carriers'][] = [
                'key' => $def['key'],
                'label' => $this->language->get($def['label_key']),
                'selector' => $def['selector'],
                'default' => $additionalSettings[$defaultKey] ?? null,
            ];
        }
        $selectedServices = array_map(fn($service) => $service->toArray(), $this->servicesRepository->getSelectedServices());
        $data = array_merge($data, ['selected_services' => $selectedServices]);

        $pickupTypeService = new PickupTypeService($config->getRegion());
        $data['pickup_types'] = $pickupTypeService->getAllowedPickupTypes();
        $data['pickup_type_labels'] = $this->pickupLabelProvider->getPickupTypeLabels();

        if ($this->currentRegion->code === 'romania' && $config->isComplete()) {
            $structResult = $this->wrapperService->getServiceStructure($config);
            if ($structResult->isSuccess() && !empty($structResult->getData()['package_types'])) {
                $data['romania_package_types'] = $structResult->getData()['package_types'];
            }
        }
        if (empty($data['romania_package_types'])) {
            $data['romania_package_types'] = [];
        }
        if ($this->currentRegion->code === 'poland') {
            $data['poland_package_types'] = $this->apaczkaPackageTypesProvider->getPackageTypes();
            $data['poland_package_type_dimensions'] = $this->apaczkaPackageTypesProvider->getPackageDimensions();
        }
        if (empty($data['poland_package_types'])) {
            $data['poland_package_types'] = [];
        }
        $data['non_standard'] = false;
        $mainShippingTemplate = $this->shippingSettingsRepository->getMainTemplate();
        if ($mainShippingTemplate && $this->currentRegion->code === 'romania') {
            $data['non_standard'] = $mainShippingTemplate->isNonStandard();
        }

        $data['header'] = $this->load->controller('common/header');
        $data['column_left'] = $this->load->controller('common/column_left');
        $data['footer'] = $this->load->controller('common/footer');

        $data['breadcrumbs'] = [];
        $data['breadcrumbs'][] = [
            'text' => $this->language->get('text_home'),
            'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
        ];

        $data['breadcrumbs'][] = [
            'text' => 'Alsendo',
            'href' => 'javascript:void(0)'
        ];

        $data['breadcrumbs'][] = [
            'text' => $this->language->get('text_configuration_page_title'),
            'href' => $this->url->link('extension/alsendo/module/alsendo', 'user_token=' . $this->session->data['user_token'])
        ];

        $data['map_config_json'] = json_encode($this->mapWidgetService->getClientConfig());
        $data['available_tags'] = $this->packageContentTags->getAvailableTagMap();

        $countries = [];
        $countryTranslatableMap = Country::getTranslatableMap();

        foreach ($countryTranslatableMap as $isoCode => $translationKey) {
            $countries[$isoCode] = $this->language->get($translationKey);
        }

        $data['region'] = $this->currentRegion->code ?? '';
        $data['text_shipment_packaging_non_standard'] = $this->language->get('text_shipment_packaging_non_standard');

        asort($countries);
        $data['countries'] = $countries;

        $this->response->setOutput($this->load->view('extension/alsendo/module/alsendo', $data));
    }

    public function apiConnectionCheck(): void
    {
        $this->load->language('extension/alsendo/main');

        if (!$this->user->hasPermission('access', 'extension/alsendo/module/alsendo')) {
            $this->sendJsonResponse([
                'success' => false,
                'error' => $this->language->get('error_permission'),
            ]);
            return;
        }

        $config = $this->configRepository->getConfig();
        $ok = false;

        if ($config->isComplete()) {
            $ok = (bool)$this->wrapperService->validateApiConnection($config);
        }

        $this->sendJsonResponse([
            'success' => $ok,
        ]);
    }

    public function orderShipmentMapping(): void {
        $this->load->language('extension/alsendo/main');
        $this->document->setTitle($this->language->get('menu_mapping_config_title'));

        $this->document->addStyle('/extension/alsendo/admin/view/css/style.css');

        if (!$this->user->hasPermission('access', 'extension/alsendo/module/alsendo')) {
            $this->response->redirect($this->url->link('error/permission', 'user_token=' . $this->session->data['user_token']));
        }

        $config = $this->configRepository->getConfig();
        $data = $this->commonDataProvider->getData($config);
        $data['current_store_currency'] = $this->config->get('config_currency');
        $data = array_merge($data, $this->senderAddressDataProvider->getData());
        $data = array_merge($data, $this->shippingSettingsDataProvider->getData());
        $data = array_merge($data, $this->additionalSettingsDataProvider->getData());
        $selectedServices = array_map(fn($service) => $service->toArray(), $this->servicesRepository->getSelectedServices());
        $data = array_merge($data, ['selected_services' => $selectedServices]);

        $data['header'] = $this->load->controller('common/header');
        $data['column_left'] = $this->load->controller('common/column_left');
        $data['footer'] = $this->load->controller('common/footer');

        $data['breadcrumbs'] = [];
        $data['breadcrumbs'][] = [
            'text' => $this->language->get('text_home'),
            'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
        ];

        $data['breadcrumbs'][] = [
            'text' => 'Alsendo',
            'href' => 'javascript:void(0)'
        ];

        $data['breadcrumbs'][] = [
            'text' => $this->language->get('menu_mapping_config_title'),
            'href' => $this->url->link('extension/alsendo/module/alsendo.shipmentMapping', 'user_token=' . $this->session->data['user_token'])
        ];

        $this->load->model('localisation/order_status');
        $this->load->model('extension/alsendo/module/alsendo_shipment_status');

        $alsendoShipmentStatuses = $this->model_extension_alsendo_module_alsendo_shipment_status->getCurrentRegionShipmentStatuses();

        $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
        $data['shipment_trigger_status_id'] = $this->model_extension_alsendo_module_alsendo_shipment_status->getShipmentTriggerStatusId();
        $data['alsendo_shipment_statuses'] = $alsendoShipmentStatuses;

        $this->response->setOutput($this->load->view('extension/alsendo/module/alsendo_order_shipment_mapping_config', $data));
    }

    public function updateShipmentStatusToOrderStatusMappings(): void {
        $payload = $this->request->post;

        $shipmentTriggerStatusId = !empty($payload['shipment_trigger_status_id']) ? (int)$payload['shipment_trigger_status_id'] : null;
        $shipmentStatusToOrderStatusMap = [];
        foreach ($payload['shipment_status_to_order_status_map'] as $shipmentId => $shipmentStatusToOrderStatusMapItem) {
            $preparedValue = !empty($shipmentStatusToOrderStatusMapItem) ? (int)$shipmentStatusToOrderStatusMapItem : null;
            $shipmentStatusToOrderStatusMap[$shipmentId] = $preparedValue;
        }

        $this->load->model('extension/alsendo/module/alsendo_shipment_status');
        $this->model_extension_alsendo_module_alsendo_shipment_status->setShipmentTriggerStatusId($shipmentTriggerStatusId);
        $this->model_extension_alsendo_module_alsendo_shipment_status->updateMappings($shipmentStatusToOrderStatusMap);
    }

    public function extendAdminMenu(string $route, array &$data): void {
        $this->load->language('extension/alsendo/main');

        $data['menus'][] = [
            'id'       => 'menu-alsendo',
            'icon'     => 'fas fa-shipping-fast',
            'name'     => 'Alsendo',
            'href'     => '',
            'children' => [
                [
                    'name' => $this->language->get('menu_main_config_title'),
                    'href' => '',
                    'children' => [
                        [
                            'name' => $this->language->get('menu_main_config_title'),
                            'href' => $this->url->link('extension/alsendo/module/alsendo', 'user_token=' . $this->session->data['user_token'], true),
                            'children' => [],
                        ],
                        [
                            'name' => $this->language->get('menu_shipping_config_title'),
                            'href' => $this->url->link('extension/alsendo/alsendo_shipment', 'user_token=' . $this->session->data['user_token'], true),
                            'children' => []
                        ],
                    ]
                ],
                [
                    'name' => $this->language->get('text_alsendo_logs_menu'),
                    'href' => $this->url->link(
                        'extension/alsendo/module/alsendo.showLogs',
                        'user_token=' . $this->session->data['user_token'],
                        true
                    ),
                    'children' => []
                ],
            ]
        ];
    }

    public function showLogs(): void
    {
        $data['user_token'] = $this->session->data['user_token'];

        $data['header'] = $this->load->controller('common/header');
        $data['column_left'] = $this->load->controller('common/column_left');
        $data['footer'] = $this->load->controller('common/footer');

        $this->load->model('extension/alsendo/module/log');
        $data['logs'] = $this->model_extension_alsendo_module_log->getLogs();

        $this->response->setOutput($this->load->view('extension/alsendo/module/alsendo_logs', $data));
    }

    private function handleAjaxActions(CoreConfigurationRequestDTO $request): void
    {
        switch ($request->action) {
            case 'save_region_and_api_keys':
                $this->handleSaveRegionAndApiKeys($request);
                break;
            case 'initialize_wrapper':
                $this->handleInitializeWrapper();
                break;
            case 'test_wrapper':
                $this->handleTestWrapper();
                break;
            case 'save_sender_template':
                $this->handleSaveSenderTemplate(SaveSenderTemplateRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'update_sender_template':
                $this->handleUpdateSenderTemplate(UpdateSenderTemplateRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'delete_sender_template':
                $this->handleDeleteSenderTemplate(DeleteTemplateRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'set_template_as_main':
                $this->handleSetTemplateAsMain(SetTemplateAsMainRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'get_template_data':
                $this->handleGetTemplateData(GetTemplateDataRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'save_shipping_settings_template':
                $this->handleSaveShippingSettingsTemplate(SaveShippingSettingsTemplateRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'update_shipping_settings_template':
                $this->handleUpdateShippingSettingsTemplate(UpdateShippingSettingsTemplateRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'delete_shipping_settings_template':
                $this->handleDeleteShippingSettingsTemplate(DeleteTemplateRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'set_shipping_settings_template_as_main':
                $this->handleSetShippingSettingsTemplateAsMain(SetTemplateAsMainRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'get_shipping_settings_template_data':
                $this->handleGetShippingSettingsTemplateData(GetTemplateDataRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'save_additional_settings':
                $this->handleSaveAdditionalSettings(SaveAdditionalSettingsRequestDTO::fromCoreConfigurationRequestDTO($request));
                break;
            case 'ecolet_authorize_url':
                $this->handleEcoletAuthorizeUrl($request);
                break;
            default:
                $this->sendJsonResponse(['error' => 'Invalid action']);
        }
    }

    private function handleEcoletAuthorizeUrl(CoreConfigurationRequestDTO $request): void
    {
        $clientId = $request->roClientId ?? null;
        $clientSecret = $request->roClientSecret ?? null;
        $roSandbox = $request->roSandbox ?? '';

        if (!empty($clientId) && !empty($clientSecret)) {
            $this->regionHandler->syncRegion('romania');
            $apiConfig = [
                'ro_client_id' => $clientId,
                'ro_client_secret' => $clientSecret,
                'ro_sandbox' => !empty($roSandbox) ? '1' : '0',
            ];
            $saveResult = $this->configHandler->saveApiConfig($apiConfig);
            if (isset($saveResult['error'])) {
                $this->sendJsonResponse(['error' => $saveResult['error']]);
                return;
            }
        }

        $config = $this->configRepository->getConfig();
        $clientId = $config->getRoClientId();
        $clientSecret = $config->getRoClientSecret();
        if (empty($clientId) || empty($clientSecret)) {
            $this->sendJsonResponse(['error' => 'Ecolet client ID and secret are required']);
            return;
        }
        $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'];
        $baseUrl = $scheme . '://' . $host;
        $redirectUri = $baseUrl . '/index.php?route=extension/alsendo/ecolet_auth';
        if (str_contains($baseUrl, 'localhost') || str_contains($baseUrl, '127.0.0.1')) {
            $redirectUri = 'https://google.com';
        }
        $state = md5(substr($clientSecret, 0, 10));
        require_once(DIR_EXTENSION . 'alsendo/wrapper/vendor/autoload.php');
        $oauthConfig = [
            'client_id' => $clientId,
            'client_secret' => $clientSecret,
            'redirect_uri' => $redirectUri,
        ];
        $test = $config->getRoSandbox();
        $client = new \Alsendo\AlsendoWrapper\Api\Ecolet\EcoletOAuthClient($oauthConfig, $test);
        $url = $client->getAuthorizationUrl($state);
        $this->sendJsonResponse(['url' => $url]);
    }

    private function handleSaveRegionAndApiKeys(CoreConfigurationRequestDTO $request): void
    {
        $selectedRegion = $request->region ?? '';
        $this->regionHandler->syncRegion($selectedRegion);

        $apiKeysRequest = SaveApiKeysRequestDTO::fromCoreConfigurationRequestDTO($request);
        $apiConfig = [
            'app_id' => $apiKeysRequest->appId ?? '',
            'app_secret' => $apiKeysRequest->appSecret ?? '',
            'token' => $apiKeysRequest->token ?? '',
            'api_key' => $apiKeysRequest->apiKey ?? '',
            'ro_client_id' => $apiKeysRequest->roClientId ?? '',
            'ro_client_secret' => $apiKeysRequest->roClientSecret ?? '',
            'sandbox' => $apiKeysRequest->sandbox ?? '',
            'ro_sandbox' => $apiKeysRequest->roSandbox ?? '',
            'cz_sandbox' => $apiKeysRequest->czSandbox ?? '',
        ];

        $apiConfig = array_filter($apiConfig, function($value) {
            return !empty($value);
        });

        if ($request->region === 'poland' && empty($apiConfig['sandbox'])) {
            $apiConfig['sandbox'] = '0';
        }

        if ($request->region === 'romania' && empty($apiConfig['ro_sandbox'])) {
            $apiConfig['ro_sandbox'] = '0';
        }

        if ($request->region === 'czechia' && empty($apiConfig['cz_sandbox'])) {
            $apiConfig['cz_sandbox'] = '0';
        }

        $result = $this->configHandler->saveApiConfig($apiConfig);
        $this->sendJsonResponse($result);
    }

    private function handleInitializeWrapper(): void
    {
        $config = $this->configRepository->getConfig();
        $result = $this->wrapperService->initializeWrapper($config);
        $this->sendJsonResponse($result->toArray());
    }

    private function handleTestWrapper(): void
    {
        $config = $this->configRepository->getConfig();
        $result = $this->wrapperService->testWrapper($config);
        $this->sendJsonResponse($result->toArray());
    }

    private function sendJsonResponse(array $data): void
    {
        $this->response->addHeader('Content-Type: application/json');
        $this->response->setOutput(json_encode($data));
    }

    private function handleSaveSenderTemplate(SaveSenderTemplateRequestDTO $request): void
    {
        $result = $this->senderAddressHandler->saveTemplate($request);
        $this->sendJsonResponse($result);
    }

    private function handleUpdateSenderTemplate(UpdateSenderTemplateRequestDTO $request): void
    {
        $result = $this->senderAddressHandler->updateTemplate($request);
        $this->sendJsonResponse($result);
    }

    private function handleDeleteSenderTemplate(DeleteTemplateRequestDTO $request): void
    {
        $id = $request->id ?? 0;
        $result = $this->senderAddressHandler->deleteTemplate($id);
        $this->sendJsonResponse($result);
    }

    private function handleSetTemplateAsMain(SetTemplateAsMainRequestDTO $request): void
    {
        $id = $request->id ?? 0;
        $result = $this->senderAddressHandler->setAsMain($id);
        $this->sendJsonResponse($result);
    }

    private function handleGetTemplateData(GetTemplateDataRequestDTO $request): void
    {
        $id = $request->id ?? 0;
        $template = $this->senderAddressRepository->getTemplateById($id);

        if ($template) {
            $this->sendJsonResponse(['success' => true, 'data' => $template->toArray()]);
        } else {
            $this->sendJsonResponse(['error' => 'Template not found']);
        }
    }

    private function handleSaveShippingSettingsTemplate(SaveShippingSettingsTemplateRequestDTO $request): void
    {
        $result = $this->shippingSettingsHandler->saveTemplate($request);
        $this->sendJsonResponse($result);
    }

    private function handleUpdateShippingSettingsTemplate(UpdateShippingSettingsTemplateRequestDTO $request): void
    {
        $result = $this->shippingSettingsHandler->updateTemplate($request);
        $this->sendJsonResponse($result);
    }

    private function handleDeleteShippingSettingsTemplate(DeleteTemplateRequestDTO $request): void
    {
        $id = $request->id ?? 0;
        $result = $this->shippingSettingsHandler->deleteTemplate($id);
        $this->sendJsonResponse($result);
    }

    private function handleSetShippingSettingsTemplateAsMain(SetTemplateAsMainRequestDTO $request): void
    {
        $id = $request->id ?? 0;
        $result = $this->shippingSettingsHandler->setAsMain($id);
        $this->sendJsonResponse($result);
    }

    private function handleGetShippingSettingsTemplateData(GetTemplateDataRequestDTO $request): void
    {
        $id = $request->id ?? 0;
        $template = $this->shippingSettingsRepository->getTemplateById($id);

        if ($template) {
            $data = $template->toArray();

            if ($request->autoCompleteContentForOrderId) {
                $this->load->model('sale/order');
                $orderInfo = $this->model_sale_order->getOrder($request->autoCompleteContentForOrderId);

                $data['shipment_content'] = $this->packageContentTags->generatePackageContent($orderInfo, $data);
            }

            $this->sendJsonResponse(['success' => true, 'data' => $data]);
        } else {
            $this->sendJsonResponse(['error' => 'Template not found']);
        }
    }

    private function handleSaveAdditionalSettings(SaveAdditionalSettingsRequestDTO $request): void
    {
        $result = $this->additionalSettingsHandler->saveSettings($request);

        $this->sendJsonResponse($result);
    }

    public function getOrderSectionData(): void
    {
        $this->load->language('extension/alsendo/main');

        $orderId = (int)($this->request->get['order_id'] ?? 0);
        if (!$orderId) {
            $this->sendJsonResponse(['success' => false, 'error' => 'Order ID not provided']);
            return;
        }

        try {
            $this->load->model('sale/order');
            $orderInfo = $this->model_sale_order->getOrder($orderId);

            if (!$orderInfo) {
                $this->sendJsonResponse(['success' => false, 'error' => 'Order not found']);
                return;
            }

            $orderProducts = $this->model_sale_order->getProducts($orderId);

            require_once(DIR_EXTENSION . 'alsendo/src/Models/OrderData.php');
            require_once(DIR_EXTENSION . 'alsendo/src/Providers/OrderSectionDataProvider.php');

            $orderData = new \Opencart\Extension\Alsendo\Models\OrderData($orderInfo, $orderProducts);
            $orderSectionDataProvider = new \Opencart\Extension\Alsendo\Providers\OrderSectionDataProvider($this->senderAddressRepository, $this->shippingSettingsRepository);
            $data = $orderSectionDataProvider->getOrderSectionData($orderData);

            $this->sendJsonResponse(['success' => true, 'data' => $data]);
        } catch (\Exception $e) {
            $this->sendJsonResponse(['success' => false, 'error' => 'Error loading order data: ' . $e->getMessage()]);
        }
    }

    public function addOrderSection(string $route, array &$data, mixed &$output): void
    {
        if ($route == 'sale/order_info') {

            $config = $this->configRepository->getConfig();
            if (!$config->isComplete()) {
                return;
            }

            $this->load->language('extension/alsendo/main');

            $orderId = $data['order_id'] ?? 0;

            if (empty($orderId)) {
                return;
            }

            $this->registry->load->model('extension/alsendo/module/alsendo_order_details');
            /** @var OrderDetailsEntity $orderDetails */
            $orderDetails = $this->model_extension_alsendo_module_alsendo_order_details->getOrderDetailsByOrderId($orderId);

            if ($orderDetails === null) {
                $createFactory = new CreateOrderDetailsFactory($this->registry);
                $createOrderDetails = $createFactory->getCreateOrderDetailsForRegion($this->currentRegion);
                $createOrderDetails->createIfNotExists((int) $orderId, false, $this->currentRegion);

                /** @var OrderDetailsEntity $orderDetails */
                $orderDetails = $this->model_extension_alsendo_module_alsendo_order_details->getOrderDetailsByOrderId($orderId);
            }

            $orderDetails->packageDetails->setPickupLabelProvider($this->pickupLabelProvider);

            $orderShipment = $this->orderShipmentProvider->getLatestNotCancelledByOrderId((int) $orderId);

            $sectionHtml = '';
            if ($orderDetails->region->id === $this->currentRegion->id) {
                $sectionHtml = $this->getAlsendoOrderInfoSupplement(
                    $orderId,
                    $this->session->data['user_token'],
                    $orderDetails,
                    $orderShipment
                );
            } else {
                if ($orderDetails->region) {
                    $sectionHtml = $this->load->view('extension/alsendo/supplements/order_section_wrong_region', [
                        'order_region_name' => $this->language->get(sprintf('text_country_name_%s', $orderDetails->region->code)),
                        'current_region_name' => $this->language->get(sprintf('text_country_name_%s', $this->currentRegion->code))
                    ]);
                }
            }

            $scriptTagCombobox = '<script src="/extension/alsendo/admin/view/javascript/combobox.js"></script>';
            $scriptTagQuote = '<script src="/extension/alsendo/admin/view/javascript/get-quote.js"></script>';
            $cssTag = '<link type="text/css" href="/extension/alsendo/admin/view/css/style.css" rel="stylesheet" media="screen"/>';

            if (str_contains($output, '</head>')) {
                $output = str_replace('</head>', $cssTag.$scriptTagCombobox.$scriptTagQuote.'</head>', $output);
            }

            if (preg_match('/<div[^>]*id=["\']content["\'][^>]*>/', $output, $matches, PREG_OFFSET_CAPTURE)) {
                $contentStart = $matches[0][1];
                $pos = strpos($output, '>', $contentStart) + 1;
                $divCount = 1;

                while ($divCount > 0 && $pos < strlen($output)) {
                    $nextOpenDiv = strpos($output, '<div', $pos);
                    $nextCloseDiv = strpos($output, '</div>', $pos);

                    if ($nextCloseDiv !== false && ($nextOpenDiv === false || $nextCloseDiv < $nextOpenDiv)) {
                        $divCount--;
                        if ($divCount == 0) {
                            $output = substr($output, 0, $nextCloseDiv) . $sectionHtml . substr($output, $nextCloseDiv);
                            break;
                        }
                        $pos = $nextCloseDiv + 6;
                    } elseif ($nextOpenDiv !== false) {
                        $divCount++;
                        $pos = $nextOpenDiv + 4;
                    } else {
                        break;
                    }
                }
            } else {
                $output = str_replace('{{ footer }}', $sectionHtml . '{{ footer }}', $output);
            }
        }
    }

    private function getAlsendoOrderInfoSupplement($orderId, $userToken, $orderDetails, $orderShipment): string {
        $this->load->model('sale/order');
        $orderInfo = $this->model_sale_order->getOrder($orderId);
        $updateOrderDetails = new UpdateOrderDetailsFromOpenCartOrder($this->registry, $this->db);
        $updateOrderDetails->sync($orderInfo, $orderDetails);

        $isPickup = ($orderDetails->packageDetails?->pickupType ?? null) === 'COURIER';

        $additionalSettings = $this->additionalSettingsDataProvider->getData()['additional_settings'] ?? [];

        $defaultPickupHourFrom = $additionalSettings['pickup_hour_from'] ?? null;
        $defaultPickupHourTo = $additionalSettings['pickup_hour_to'] ?? null;

        $commonData = [
            'order_id' => $orderId,
            'user_token' => $userToken,
            'order_details' => $orderDetails,
            'is_pickup' => $isPickup,
            'default_pickup_hour_from' => $defaultPickupHourFrom,
            'default_pickup_hour_to' => $defaultPickupHourTo,
        ];

        $orderPickupPoint = $this->orderPickupPointProvider->getOrderPickupPointByOrderId($orderId);

        if ($orderShipment && ($orderShipment->getStatus() !== OrderShipmentStatus::CANCELLED)) {
            $dto = SimpleSubmittedOrderDetailsDTO::fromOrderShipmentAndDetailsArray($orderShipment->toArray(), $orderDetails, $orderInfo, $this->currentRegion->code);
            $dto->pickupPointText = $orderPickupPoint?->getName();
            return $this->load->view(
                'extension/alsendo/supplements/order_shipment_section',
                $commonData + [
                    'dto' => $dto,
                ]
            );
        }

        $this->load->language('extension/alsendo/main');

        return $this->load->view(
            'extension/alsendo/supplements/order_section',
            $commonData + [
                'full_form_url' => $this->url->link(
                    'extension/alsendo/alsendo_order.showFullForm',
                    'user_token=' . $userToken . '&order_id=' . $orderId,
                ),
                'sender_address_templates' => array_map(fn($t) => $t->toArray(), $this->senderAddressRepository->getAllTemplates()),
                'shipping_settings_templates' => array_map(fn($t) => $t->toArray(), $this->shippingSettingsRepository->getAllTemplates()),
                'url_apply_order_sender_template' => $this->url->link('extension/alsendo/module/alsendo.applyOrderSenderTemplate', 'user_token=' . $userToken),
                'url_apply_order_package_template' => $this->url->link('extension/alsendo/module/alsendo.applyOrderPackageTemplate', 'user_token=' . $userToken),
                'text_operation_not_successful' => $this->language->get('text_operation_not_successful'),
            ]
        );
    }

    public function applyOrderSenderTemplate(): void
    {
        $this->load->language('extension/alsendo/main');
        $orderId = (int)($this->request->post['order_id'] ?? 0);
        $templateId = (int)($this->request->post['template_id'] ?? 0);
        try {
            $handler = new OrderUpdateTemplates($this->registry, $this->db, \DB_PREFIX);
            $handler->updateSenderTemplate($orderId, $templateId);
            $this->sendJsonResponse(['success' => true, 'message' => $this->language->get('text_success')]);
        } catch (\Throwable $e) {
            $this->sendJsonResponse(['success' => false, 'error' => $e->getMessage()]);
        }
    }

    public function applyOrderPackageTemplate(): void
    {
        $this->load->language('extension/alsendo/main');
        $orderId = (int)($this->request->post['order_id'] ?? 0);
        $templateId = (int)($this->request->post['template_id'] ?? 0);
        $packageIndex = (int)($this->request->post['package_index'] ?? 0);
        try {
            $handler = new OrderUpdateTemplates($this->registry, $this->db, \DB_PREFIX);
            $handler->updatePackageTemplate($orderId, $templateId, $packageIndex);
            $this->sendJsonResponse(['success' => true, 'message' => $this->language->get('text_success')]);
        } catch (\Throwable $e) {
            $this->sendJsonResponse(['success' => false, 'error' => $e->getMessage()]);
        }
    }
}