<?php

namespace Core\App\Controllers;

use Core\App\Models\Settings;
use Core\App\Models\ShippingPriceModel;
use Core\App\Models\ShippingIntegrationModel;
use Core\App\Support\ShippingSettings;
use Core\App\Support\DzLocation;
use Core\System\Response;

class SettingsController
{
    /**
     * =========================
     * Basic Settings (Database)
     * =========================
     */
    public function basic()
    {
        $settingsModel = new Settings();
        $rows = $settingsModel->getAll();

        $basicSettings = $rows;

        extract(['basicSettings' => $basicSettings]);

        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/settings/basic_settings.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    public function save()
    {
        $settingsModel = new Settings();

        $isMailTest = isset($_POST['_mail_test']) && (string)$_POST['_mail_test'] === '1';

        /* ==========================
           Mail Test (NO SAVE)
        ========================== */
        if ($isMailTest) {
            $to = trim((string)($_POST['admin_email'] ?? ''));

            $sent = false;

            $mailerPath = ROOT_PATH . '/core/system/Mailer.php';
            if (file_exists($mailerPath)) {
                require_once $mailerPath;
            }

            $mailerClass = 'Core\\System\\Mailer';
            if ($to !== '' && class_exists($mailerClass)) {
                try {
                    $mailer = new $mailerClass();
                    $sent = (bool)$mailer->send(
                        $to,
                        'Test Email',
                        '<p>This is a test email from MTEngine.</p>'
                    );
                } catch (\Throwable $e) {
                    $sent = false;
                }
            }

            Response::redirect('/admin/settings/basic?mail_test=' . ($sent ? 'success' : 'error'));
        }

        /* ==========================
           CRON SETTINGS (GLOBAL)
        ========================== */

        $existing = $settingsModel->getAll();

        $cronEnabled = isset($_POST['cron_enabled']) ? 1 : 0;
        $currentToken = $existing['cron_token'] ?? '';

        if (isset($_POST['regenerate_cron_token']) || empty($currentToken)) {
            $cronToken = bin2hex(random_bytes(16));
        } else {
            $cronToken = $currentToken;
        }

        $_POST['cron_enabled'] = $cronEnabled;
        $_POST['cron_token']   = $cronToken;

        /* ==========================
           Allowlist — only save known, safe setting keys
           This prevents mass-assignment of arbitrary keys (license_cache, etc.)
        ========================== */
        $allowedKeys = [
            // General
            'site_name', 'site_tagline', 'site_description', 'site_keywords',
            'admin_email', 'contact_phone', 'contact_email', 'contact_whatsapp', 'address',
            'copyright_text',
            'currency', 'default_language', 'timezone_offset', 'app_timezone', 'country',
            // Mail
            'mail_driver', 'mail_host', 'mail_port', 'mail_username', 'mail_password',
            'mail_encryption', 'mail_from_email', 'mail_from_name',
            // Cron
            'cron_enabled', 'cron_token', 'regenerate_cron_token',
            // SEO
            'robots_txt_content', 'seo_default_image',
            // Social
            'social_facebook', 'social_instagram', 'social_twitter',
            'social_youtube', 'social_linkedin', 'social_tiktok', 'social_whatsapp',
            'social_snapchat', 'social_telegram', 'facebook_app_id',
            // Order/Checkout
            'order_confirmation_enabled', 'whatsapp_order_notification',
            'order_success_message', 'order_notify_admin',
            // Misc display
            'favicon', 'preloader', 'show_out_of_stock',
            'products_per_page', 'maintenance_mode_frontend', 'product_image_autorotate',
        ];

        /* ==========================
           Normal Save
        ========================== */
        foreach ($_POST as $key => $value) {
            if ($key === '_mail_test' || $key === '_csrf') {
                continue;
            }

            // Only save explicitly allowed keys
            if (!in_array($key, $allowedKeys, true)) {
                continue;
            }

            $settingsModel->set(
                $key,
                is_array($value)
                    ? json_encode($value, JSON_UNESCAPED_UNICODE)
                    : trim((string)$value)
            );
        }

        Response::redirect('/admin/settings/basic?saved=1');
    }

    /**
     * =========================
     * Shipping Settings (JSON)
     * =========================
     */
    public function shipping()
    {
        $file = ROOT_PATH . '/storage/settings/shipping.json';

        $shipping = ShippingSettings::load();

        extract(['shipping' => $shipping]);

        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/settings/shipping_settings.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /**
     * =========================
     * Save Shipping Settings
     * =========================
     */
    public function saveShipping()
    {
        $data = $_POST;

        /* =========================
           NORMALIZE SHIPPING DATA
           (HOME / DESK SUPPORT)
        ========================= */

        // -------- FLAT MODE --------
        if (($data['mode'] ?? '') === 'flat') {

            // Preserve empty string as null so the product page can show the
            // "shipping unavailable" custom text instead of treating 0 as Free.
            // Only cast to float when the admin actually typed a number.
            $flatRaw     = (string)($data['flat_price']      ?? '');
            $flatHomeRaw = (string)($data['flat_home_price'] ?? '');
            $flatDeskRaw = (string)($data['flat_desk_price'] ?? '');

            $data['flat_price']      = $flatRaw     !== '' ? (float)$flatRaw     : null;
            $data['flat_home_price'] = $flatHomeRaw !== '' ? (float)$flatHomeRaw : null;
            $data['flat_desk_price'] = $flatDeskRaw !== '' ? (float)$flatDeskRaw : null;
        }

        // -------- STATE MODE --------
        if (($data['mode'] ?? '') === 'state' && !empty($data['states'])) {

            foreach ($data['states'] as $stateCode => $row) {

                if (!is_array($row)) {
                    // Legacy scalar value — preserve empty as null
                    $price = (string)$row !== '' ? (float)$row : null;

                    $data['states'][$stateCode] = [
                        'home' => $price,
                        'desk' => $price,
                    ];
                } else {
                    $homeRaw = (string)($row['home'] ?? '');
                    $deskRaw = (string)($row['desk'] ?? '');

                    $data['states'][$stateCode] = [
                        'home' => $homeRaw !== '' ? (float)$homeRaw : null,
                        'desk' => $deskRaw !== '' ? (float)$deskRaw : null,
                    ];
                }
            }
        }

        // -------- ZONES MODE --------
        if (($data['mode'] ?? '') === 'zones' && !empty($data['zones'])) {

            foreach ($data['zones'] as $i => $zone) {

                $legacyRaw   = (string)($zone['price']      ?? '');
                $homeRaw     = (string)($zone['home_price'] ?? '');
                $deskRaw     = (string)($zone['desk_price'] ?? '');

                $legacyPrice = $legacyRaw !== '' ? (float)$legacyRaw : null;

                $data['zones'][$i]['price']      = $legacyPrice;
                $data['zones'][$i]['home_price'] = $homeRaw !== '' ? (float)$homeRaw : $legacyPrice;
                $data['zones'][$i]['desk_price'] = $deskRaw !== '' ? (float)$deskRaw : $legacyPrice;
            }
        }


        /* =========================
           NORMALIZE ORIGIN (STORE LOCATION)
        ========================= */
        $originStateCode = \Core\App\Support\DzLocation::normalizeCode($data['origin']['state_code'] ?? '');
        $originCityId    = (string)($data['origin']['city_id'] ?? '');

        $originState = $originStateCode !== ''
            ? \Core\App\Support\DzLocation::findState($originStateCode)
            : null;

        $originCity = null;
        if ($originStateCode !== '' && $originCityId !== '') {
            $originCity = \Core\App\Support\DzLocation::findCityById($originStateCode, $originCityId);
        }

        if (!$originCity && $originStateCode !== '' && !empty($data['origin']['city_name'])) {
            $originCity = \Core\App\Support\DzLocation::findCityByName(
                $originStateCode,
                (string)($data['origin']['city_name'] ?? '')
            );
            if ($originCity && empty($originCityId)) {
                $originCityId = (string)($originCity['id'] ?? '');
            }
        }

        $data['origin'] = [
            'state_code'    => $originStateCode,
            'state_name'    => (string)($originState['name'] ?? trim((string)($data['origin']['state_name'] ?? ''))),
            'state_ar_name' => (string)($originState['ar_name'] ?? trim((string)($data['origin']['state_ar_name'] ?? ''))),
            'city_id'       => $originCityId,
            'city_name'     => (string)($originCity['name'] ?? trim((string)($data['origin']['city_name'] ?? ''))),
            'city_ar_name'  => (string)($originCity['ar_name'] ?? trim((string)($data['origin']['city_ar_name'] ?? ''))),
        ];

        $existingShipping = ShippingSettings::load();
        $integrationModel = new ShippingIntegrationModel();
        $availableProviders = array_keys(array_filter($integrationModel->getAllIndexed(), static function ($row) {
            return (int)($row['is_active'] ?? 0) === 1;
        }));

        $selectedPricingProvider = trim((string)($_POST['pricing_provider'] ?? ''));
        $data['pricing_provider'] = in_array($selectedPricingProvider, $availableProviders, true) ? $selectedPricingProvider : ($existingShipping['pricing_provider'] ?? '');
        if (!in_array($data['pricing_provider'], $availableProviders, true)) {
            $data['pricing_provider'] = $availableProviders[0] ?? '';
        }

        $data['allow_customer_provider_choice'] = !empty($_POST['allow_customer_provider_choice']) ? 1 : 0;
        $selectionMode = trim((string)($_POST['provider_selection_mode'] ?? 'customer_choice'));
        if (!$data['allow_customer_provider_choice']) {
            $selectionMode = 'manual_admin';
        }
        $data['provider_selection_mode'] = in_array($selectionMode, ['forced_provider', 'customer_choice'], true)
            ? $selectionMode
            : 'manual_admin';
        $forcedProvider = trim((string)($_POST['forced_provider'] ?? ''));
        $data['forced_provider'] = ($data['provider_selection_mode'] === 'forced_provider' && in_array($forcedProvider, $availableProviders, true)) ? $forcedProvider : '';
        // Maystro frontend capabilities are enabled automatically whenever Maystro is the active storefront provider flow.
        $data['frontend_maystro_enabled'] = 1;
        $data['frontend_maystro_allow_express'] = 1;
        $data['frontend_maystro_allow_stopdesk'] = 1;
        $data['frontend_maystro_allow_pickup'] = 1;

        $selectedPricingSource = trim((string)($_POST['pricing_source'] ?? 'manual'));
        $data['pricing_source'] = in_array($selectedPricingSource, ['manual','api','reference'], true) ? $selectedPricingSource : 'manual';
        $data['pricing_fallback_manual'] = !empty($_POST['pricing_fallback_manual']) ? 1 : 0;
        $data['shipping_unavailable_text'] = trim((string)($_POST['shipping_unavailable_text'] ?? ''));
        $postedModes = is_array($_POST['provider_price_modes'] ?? null) ? $_POST['provider_price_modes'] : [];
        $data['provider_price_modes'] = is_array($existingShipping['provider_price_modes'] ?? null)
            ? $existingShipping['provider_price_modes']
            : [];
        foreach ($postedModes as $provider => $mode) {
            $mode = in_array($mode, ['manual', 'api', 'reference'], true) ? $mode : 'manual';
            $data['provider_price_modes'][$provider] = $mode;
        }
        $data['provider_imported_prices'] = is_array($existingShipping['provider_imported_prices'] ?? null)
            ? $existingShipping['provider_imported_prices']
            : [];
        $data['provider_imported_meta'] = is_array($existingShipping['provider_imported_meta'] ?? null)
            ? $existingShipping['provider_imported_meta']
            : [];

        unset($data['_csrf']);

        /* =========================
           SAVE shipping.json
        ========================= */
        $dir = ROOT_PATH . '/storage/settings';
        if (!is_dir($dir)) {
            mkdir($dir, 0777, true);
        }

        $file = $dir . '/shipping.json';

        file_put_contents(
            $file,
            json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
        );

        /* =========================
           SAVE SHIPPING PROVIDERS PRICES
        ========================= */
        if (!empty($_POST['providers_prices'])) {

            $priceModel       = new ShippingPriceModel();
            $integrationModel = new ShippingIntegrationModel();

            $providers = $integrationModel->getAllIndexed();

            foreach ($_POST['providers_prices'] as $provider => $states) {

                if (!isset($providers[$provider])) {
                    continue;
                }

                foreach ($states as $wilayaCode => $types) {

                    foreach (['home', 'desk'] as $deliveryType) {

                        if (!isset($types[$deliveryType]) || $types[$deliveryType] === '') {
                            $priceModel->deletePrice(
                                $provider,
                                \Core\App\Support\DzLocation::normalizeCode((string)$wilayaCode),
                                $deliveryType
                            );
                            continue;
                        }

                        $priceModel->save([
                            'provider'      => $provider,
                            'country_code'  => $providers[$provider]['country_code'] ?? 'DZ',
                            'wilaya_code'   => $wilayaCode,
                            'wilaya_name'   => (\Core\App\Support\DzLocation::stateByCode($wilayaCode)['name'] ?? $wilayaCode),
                            'delivery_type' => $deliveryType,
                            'price'         => (float)$types[$deliveryType],
                            'return_price'  => 0,
                            'is_active'     => 1,
                        ]);
                    }
                }
            }
        }

        header('Location: /admin/settings/shipping?saved=1');
        exit;
    }


    public function syncShippingProviderPrices()
    {
        header('Content-Type: application/json; charset=utf-8');

        $provider = trim((string)($_POST['provider'] ?? ''));
        if ($provider === '') {
            echo json_encode(['success' => false, 'message' => __('shipping.provider_missing', 'Shipping provider is required')]);
            return;
        }

        $integration = (new ShippingIntegrationModel())->getByProvider($provider);
        if (!$integration || (int)($integration['is_active'] ?? 0) !== 1) {
            echo json_encode(['success' => false, 'message' => __('shipping.integration_disabled', 'Shipping integration is disabled')]);
            return;
        }

        try {
            $result = $this->fetchProviderPrices($provider, $integration);
            $shipping = ShippingSettings::load();
            $shipping['provider_imported_prices'][$provider] = $result['prices'];
            $shipping['provider_imported_meta'][$provider] = [
                'last_synced_at' => date('Y-m-d H:i:s'),
                'source' => $provider,
                'supported' => true,
                'states_count' => count($result['prices']),
            ];
            file_put_contents(ROOT_PATH . '/storage/settings/shipping.json', json_encode($shipping, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
            echo json_encode([
                'success' => true,
                'message' => __('shipping.sync_success', 'Provider prices synced successfully'),
                'prices' => $result['prices'],
                'last_synced_at' => $shipping['provider_imported_meta'][$provider]['last_synced_at'],
            ], JSON_UNESCAPED_UNICODE);
        } catch (\Throwable $e) {
            echo json_encode(['success' => false, 'message' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
        }
    }

    public function applyImportedShippingProviderPrices()
    {
        header('Content-Type: application/json; charset=utf-8');

        $provider = trim((string)($_POST['provider'] ?? ''));
        if ($provider === '') {
            echo json_encode(['success' => false, 'message' => __('shipping.provider_missing', 'Shipping provider is required')]);
            return;
        }

        $shipping = ShippingSettings::load();
        $imported = $shipping['provider_imported_prices'][$provider] ?? [];
        if (!is_array($imported) || empty($imported)) {
            echo json_encode(['success' => false, 'message' => __('shipping.no_imported_prices', 'No imported provider prices available yet')]);
            return;
        }

        $integration = (new ShippingIntegrationModel())->getByProvider($provider);
        if (!$integration) {
            echo json_encode(['success' => false, 'message' => __('shipping.invalid_provider', 'Invalid shipping provider')]);
            return;
        }

        $priceModel = new ShippingPriceModel();
        foreach ($imported as $wilayaCode => $types) {
            foreach (['home', 'desk'] as $deliveryType) {
                if (!isset($types[$deliveryType]) || !is_numeric($types[$deliveryType])) {
                    continue;
                }
                $state = DzLocation::stateByCode($wilayaCode);
                $priceModel->save([
                    'provider' => $provider,
                    'country_code' => $integration['country_code'] ?? 'DZ',
                    'wilaya_code' => DzLocation::normalizeCode($wilayaCode),
                    'wilaya_name' => (string)($state['name'] ?? $wilayaCode),
                    'delivery_type' => $deliveryType,
                    'price' => (float)$types[$deliveryType],
                    'return_price' => 0,
                    'is_active' => 1,
                ]);
            }
        }

        echo json_encode(['success' => true, 'message' => __('shipping.apply_imported_success', 'Imported provider prices were copied to manual prices')], JSON_UNESCAPED_UNICODE);
    }

    private function fetchProviderPrices(string $provider, array $integration): array
    {
        switch ($provider) {
            case 'ecotrack':
                $baseUrl = trim((string)($integration['api_id'] ?? ''));
                $apiToken = trim((string)($integration['api_token'] ?? ''));
                if ($baseUrl === '' || $apiToken === '') {
                    throw new \RuntimeException(__('shipping.provider_missing_credentials', 'Provider credentials are incomplete'));
                }
                $client = new \Core\App\Integrations\Shipping\EcotrackClient($apiToken, $baseUrl);
                $response = $client->getFees();
                if (empty($response['success']) || !is_array($response['data'] ?? null)) {
                    throw new \RuntimeException((string)($response['error'] ?? __('shipping.sync_failed', 'Failed to sync provider prices')));
                }
                return ['prices' => $this->normalizeEcotrackFees($response['data'])];

            case 'maystro':
                $apiToken = trim((string)($integration['api_token'] ?? ''));
                $baseUrl  = trim((string)($integration['api_id']    ?? ''));
                if ($apiToken === '') {
                    throw new \RuntimeException(__('shipping.provider_missing_credentials', 'Maystro API token is missing'));
                }
                $client   = new \Core\App\Integrations\Shipping\MaystroClient($apiToken, $baseUrl);
                // Maystro prices are per-commune. We sample the first commune per wilaya.
                $states   = DzLocation::states();
                $prices   = [];
                foreach ($states as $state) {
                    $code     = DzLocation::normalizeCode($state['code'] ?? '');
                    $wilayaId = (int)($state['id'] ?? 0);
                    if ($code === '' || $wilayaId <= 0) continue;
                    $cRes     = $client->getCommunes($wilayaId);
                    $communes = $cRes['data'] ?? [];
                    if (empty($communes) || !is_array($communes[0] ?? null)) continue;
                    $communeId = (int)($communes[0]['id'] ?? 0);
                    if ($communeId <= 0) continue;
                    $oRes    = $client->getDeliveryOptions($communeId);
                    $options = $oRes['data'] ?? [];
                    $home = null; $desk = null;
                    foreach ((array)$options as $opt) {
                        if (($opt['type'] ?? '') === 'home')     $home = (float)($opt['price'] ?? 0);
                        if (($opt['type'] ?? '') === 'stopdesk') $desk = (float)($opt['price'] ?? 0);
                    }
                    $prices[$code] = ['home' => $home, 'desk' => $desk];
                }
                if (empty($prices)) {
                    throw new \RuntimeException(__('shipping.sync_failed', 'Could not fetch Maystro delivery prices'));
                }
                return ['prices' => $prices];


            default:
                throw new \RuntimeException(__('shipping.pricing_not_supported', 'Automatic pricing not supported'));
        }
    }

    private function normalizeEcotrackFees(array $payload): array
    {
        $collection = [];
        if (isset($payload['Livraison']) && is_array($payload['Livraison'])) {
            $collection = $payload['Livraison'];
        } elseif (isset($payload['livraison']) && is_array($payload['livraison'])) {
            $collection = $payload['livraison'];
        } elseif (isset($payload['data']['Livraison']) && is_array($payload['data']['Livraison'])) {
            $collection = $payload['data']['Livraison'];
        } elseif (isset($payload['data']['livraison']) && is_array($payload['data']['livraison'])) {
            $collection = $payload['data']['livraison'];
        } elseif (isset($payload[0])) {
            $collection = $payload;
        }

        $mapped = [];
        foreach ($collection as $row) {
            if (!is_array($row)) continue;
            $code = DzLocation::normalizeCode((string)($row['wilaya_id'] ?? $row['wilaya'] ?? $row['id'] ?? ''));
            if ($code === '') continue;
            $mapped[$code] = [
                'home' => isset($row['tarif']) && is_numeric($row['tarif']) ? (float)$row['tarif'] : null,
                'desk' => isset($row['tarif_stopdesk']) && is_numeric($row['tarif_stopdesk']) ? (float)$row['tarif_stopdesk'] : null,
            ];
        }
        return $mapped;
    }

    private function normalizeMaystroFees(array $data): array
    {
        $mapped = [];
        foreach ($data as $row) {
            if (!is_array($row)) continue;
            $code = '';
            if (!empty($row['wilaya_code'])) {
                $code = DzLocation::normalizeCode((string)$row['wilaya_code']);
            } elseif (!empty($row['wilaya_id'])) {
                $code = DzLocation::normalizeCode((string)$row['wilaya_id']);
            } elseif (!empty($row['code'])) {
                $code = DzLocation::normalizeCode((string)$row['code']);
            }
            if ($code === '' && !empty($row['wilaya'])) {
                $state = DzLocation::stateByName((string)$row['wilaya']);
                if ($state) {
                    $code = DzLocation::normalizeCode((string)($state['code'] ?? ''));
                }
            }
            if ($code === '') continue;
            $homePrice = null;
            $deskPrice = null;
            foreach (['home_price', 'home', 'tarif_domicile', 'tarif'] as $field) {
                if (isset($row[$field]) && is_numeric($row[$field])) {
                    $homePrice = (float)$row[$field];
                    break;
                }
            }
            foreach (['desk_price', 'desk', 'tarif_stopdesk', 'tarif_bureau'] as $field) {
                if (isset($row[$field]) && is_numeric($row[$field])) {
                    $deskPrice = (float)$row[$field];
                    break;
                }
            }
            $mapped[$code] = ['home' => $homePrice, 'desk' => $deskPrice];
        }
        return $mapped;
    }

}