<?php

namespace App\Services;

use App\DTO\Settings\AppDTO;
use App\DTO\Settings\ConfigDTO;
use App\DTO\Settings\DatabaseDTO;
use App\DTO\Settings\LogsDTO;
use App\DTO\Settings\MetricsDTO;
use App\DTO\Settings\OrdersDTO;
use App\DTO\Settings\StoreDTO;
use App\DTO\Settings\TelegramDTO;
use App\DTO\Settings\TextsDTO;
use InvalidArgumentException;

class SettingsSerializerService
{
    public function fromArray(array $data): ConfigDTO
    {
        $keys = ['app', 'telegram', 'metrics', 'store', 'orders', 'texts', 'database', 'logs'];
        foreach ($keys as $key) {
            if (! array_key_exists($key, $data)) {
                throw new InvalidArgumentException("Settings key '$key' is required!");
            }
        }

        $this->validateApp($data['app']);
        $this->validateTelegram($data['telegram']);
        $this->validateMetrics($data['metrics']);
        $this->validateStore($data['store']);
        $this->validateOrders($data['orders']);
        $this->validateTexts($data['texts']);
        $this->validateDatabase($data['database']);
        $this->validateLogs($data['logs']);

        return new ConfigDTO(
            $this->deserializeApp($data['app']),
            $this->deserializeTelegram($data['telegram']),
            $this->deserializeMetrics($data['metrics']),
            $this->deserializeStore($data['store']),
            $this->deserializeOrders($data['orders']),
            $this->deserializeTexts($data['texts']),
            $this->deserializeDatabase($data['database']),
            $this->deserializeLogs($data['logs']),
        );
    }

    /**
     * @throws \JsonException
     */
    public function serialize(ConfigDTO $settings): string
    {
        return json_encode($settings->toArray(), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }


    private function deserializeApp(array $data): AppDTO
    {
        if (! isset($data['language_id'])) {
            throw new InvalidArgumentException('app.language_id is required');
        }

        if (! is_numeric($data['language_id'])) {
            throw new InvalidArgumentException('app.language_id must be an integer');
        }

        if (! isset($data['shop_base_url'])) {
            throw new InvalidArgumentException('app.shop_base_url is required');
        }

        if (! is_string($data['shop_base_url'])) {
            throw new InvalidArgumentException('app.shop_base_url must be a string');
        }

        return new AppDTO(
            $data['app_enabled'] ?? false,
            $data['app_name'] ?? '',
            $data['app_icon'] ?? null,
            $data['theme_light'] ?? 'light',
            $data['theme_dark'] ?? 'dark',
            $data['app_debug'] ?? false,
            $data['language_id'],
            $data['shop_base_url'],
            $data['haptic_enabled'] ?? true
        );
    }

    private function deserializeTelegram(array $data): TelegramDTO
    {
        if (! isset($data['mini_app_url'])) {
            throw new InvalidArgumentException('telegram.mini_app_url is required');
        }

        if (! is_string($data['mini_app_url'])) {
            throw new InvalidArgumentException('telegram.mini_app_url must be a string');
        }

        return new TelegramDTO(
            $data['bot_token'],
            $data['chat_id'],
            $data['owner_notification_template'],
            $data['customer_notification_template'],
            $data['mini_app_url']
        );
    }

    private function deserializeMetrics(array $data): MetricsDTO
    {
        return new MetricsDTO(
            $data['yandex_metrika_enabled'] ?? false,
            $data['yandex_metrika_counter'] ?? ''
        );
    }

    private function deserializeStore(array $data): StoreDTO
    {
        if (! isset($data['oc_default_currency'])) {
            throw new InvalidArgumentException('store.oc_default_currency is required');
        }

        if (! is_string($data['oc_default_currency'])) {
            throw new InvalidArgumentException('store.oc_default_currency must be a string');
        }

        if (! isset($data['oc_config_tax'])) {
            throw new InvalidArgumentException('store.oc_config_tax is required');
        }

        if (! is_bool($data['oc_config_tax'])) {
            throw new InvalidArgumentException('store.oc_config_tax must be a boolean');
        }

        if (! isset($data['oc_store_id'])) {
            throw new InvalidArgumentException('store.oc_store_id is required');
        }

        if (! is_numeric($data['oc_store_id'])) {
            throw new InvalidArgumentException('store.oc_store_id must be an integer');
        }

        return new StoreDTO(
            $data['feature_coupons'] ?? true,
            $data['feature_vouchers'] ?? true,
            $data['show_category_products_button'] ?? true,
            $data['product_interaction_mode'] ?? 'browser',
            $data['manager_username'] ?? null,
            $data['oc_default_currency'],
            $data['oc_config_tax'],
            $data['oc_store_id']
        );
    }

    private function deserializeOrders(array $data): OrdersDTO
    {
        if (! isset($data['oc_customer_group_id'])) {
            throw new InvalidArgumentException('orders.oc_customer_group_id is required');
        }

        if (! is_numeric($data['oc_customer_group_id'])) {
            throw new InvalidArgumentException('orders.oc_customer_group_id must be an integer');
        }

        return new OrdersDTO(
            $data['order_default_status_id'] ?? 1,
            $data['oc_customer_group_id']
        );
    }

    private function deserializeTexts(array $data): TextsDTO
    {
        return new TextsDTO(
            $data['text_no_more_products'],
            $data['text_empty_cart'],
            $data['text_order_created_success'],
            $data['text_manager_button'] ?? ''
        );
    }

    // ==================== Validation Methods ====================

    private function validateApp(array $data): void
    {
        if (! is_bool($data['app_enabled'])) {
            throw new InvalidArgumentException('app.app_enabled must be a boolean');
        }

        if (! is_string($data['app_name'])) {
            throw new InvalidArgumentException('app.app_name must be a string');
        }

        if (isset($data['app_icon']) && ! is_string($data['app_icon'])) {
            throw new InvalidArgumentException('app.app_icon must be a string or null');
        }

        if (! is_string($data['theme_light'])) {
            throw new InvalidArgumentException('app.theme_light must be a string');
        }

        if (! is_string($data['theme_dark'])) {
            throw new InvalidArgumentException('app.theme_dark must be a string');
        }

        if (! is_bool($data['app_debug'])) {
            throw new InvalidArgumentException('app.app_debug must be a boolean');
        }

        if (! isset($data['language_id'])) {
            throw new InvalidArgumentException('app.language_id is required');
        }

        if (! is_numeric($data['language_id'])) {
            throw new InvalidArgumentException('app.language_id must be an integer');
        }

        if ($data['language_id'] <= 0) {
            throw new InvalidArgumentException('app.language_id must be a positive integer');
        }

        if (! isset($data['shop_base_url'])) {
            throw new InvalidArgumentException('app.shop_base_url is required');
        }

        if (! is_string($data['shop_base_url'])) {
            throw new InvalidArgumentException('app.shop_base_url must be a string');
        }
    }

    private function validateTelegram(array $data): void
    {
        if (isset($data['bot_token']) && ! is_string($data['bot_token'])) {
            throw new InvalidArgumentException('telegram.bot_token must be a string or null');
        }

        if (isset($data['chat_id']) && ! is_numeric($data['chat_id'])) {
            throw new InvalidArgumentException('telegram.chat_id must be an integer or null');
        }

        if (
            isset($data['owner_notification_template']) && ! is_string(
                $data['owner_notification_template']
            )
        ) {
            throw new InvalidArgumentException('telegram.owner_notification_template must be a string or null');
        }

        if (
            isset($data['customer_notification_template']) && ! is_string(
                $data['customer_notification_template']
            )
        ) {
            throw new InvalidArgumentException('telegram.customer_notification_template must be a string or null');
        }

        if (! isset($data['mini_app_url'])) {
            throw new InvalidArgumentException('telegram.mini_app_url is required');
        }

        if (! is_string($data['mini_app_url'])) {
            throw new InvalidArgumentException('telegram.mini_app_url must be a string');
        }
    }

    private function validateMetrics(array $data): void
    {
        if (isset($data['yandex_metrika_enabled']) && ! is_bool($data['yandex_metrika_enabled'])) {
            throw new InvalidArgumentException('metrics.yandex_metrika_enabled must be a boolean');
        }

        if (isset($data['yandex_metrika_counter']) && ! is_string($data['yandex_metrika_counter'])) {
            throw new InvalidArgumentException('metrics.yandex_metrika_counter must be a string');
        }
    }

    private function validateStore(array $data): void
    {
        // enable_store больше не валидируется, так как заменен на product_interaction_mode

        if (isset($data['feature_coupons']) && ! is_bool($data['feature_coupons'])) {
            throw new InvalidArgumentException('store.feature_coupons must be a boolean');
        }

        if (isset($data['feature_vouchers']) && ! is_bool($data['feature_vouchers'])) {
            throw new InvalidArgumentException('store.feature_vouchers must be a boolean');
        }

        if (isset($data['show_category_products_button']) && ! is_bool($data['show_category_products_button'])) {
            throw new InvalidArgumentException('store.show_category_products_button must be a boolean');
        }

        if (isset($data['product_interaction_mode']) && ! is_string($data['product_interaction_mode'])) {
            throw new InvalidArgumentException('store.product_interaction_mode must be a string');
        }

        if (
            isset($data['product_interaction_mode'])
            && ! in_array($data['product_interaction_mode'], ['order', 'manager', 'browser'], true)
        ) {
            throw new InvalidArgumentException(
                'store.product_interaction_mode must be one of: order, manager, browser'
            );
        }

        if (isset($data['manager_username']) && $data['manager_username'] !== null) {
            if (! is_string($data['manager_username'])) {
                throw new InvalidArgumentException('store.manager_username must be a string or null');
            }
            // Проверяем, что это username (не числовой ID)
            $managerUsername = trim($data['manager_username']);
            if ($managerUsername !== '' && preg_match('/^-?\d+$/', $managerUsername)) {
                throw new InvalidArgumentException(
                    'store.manager_username must be a username (e.g., @username), not a numeric ID'
                );
            }
        }

        if (! isset($data['oc_default_currency'])) {
            throw new InvalidArgumentException('store.oc_default_currency is required');
        }

        if (! is_string($data['oc_default_currency'])) {
            throw new InvalidArgumentException('store.oc_default_currency must be a string');
        }

        if (! isset($data['oc_config_tax'])) {
            throw new InvalidArgumentException('store.oc_config_tax is required');
        }

        if (! is_bool($data['oc_config_tax'])) {
            throw new InvalidArgumentException('store.oc_config_tax must be a boolean');
        }

        if (! isset($data['oc_store_id'])) {
            throw new InvalidArgumentException('store.oc_store_id is required');
        }

        if (! is_numeric($data['oc_store_id'])) {
            throw new InvalidArgumentException('store.oc_store_id must be an integer');
        }

        if ($data['oc_store_id'] < 0) {
            throw new InvalidArgumentException('store.oc_store_id must be a positive integer or equals 0');
        }
    }

    private function validateOrders(array $data): void
    {
        if (isset($data['order_default_status_id'])) {
            if (! is_numeric($data['order_default_status_id'])) {
                throw new InvalidArgumentException('orders.order_default_status_id must be an integer');
            }
            if ($data['order_default_status_id'] <= 0) {
                throw new InvalidArgumentException('orders.order_default_status_id must be a positive integer');
            }
        }

        if (! isset($data['oc_customer_group_id'])) {
            throw new InvalidArgumentException('orders.oc_customer_group_id is required');
        }

        if (! is_numeric($data['oc_customer_group_id'])) {
            throw new InvalidArgumentException('orders.oc_customer_group_id must be an integer');
        }

        if ($data['oc_customer_group_id'] <= 0) {
            throw new InvalidArgumentException('orders.oc_customer_group_id must be a positive integer');
        }
    }

    private function validateTexts(array $data): void
    {
        if (isset($data['text_no_more_products']) && ! is_string($data['text_no_more_products'])) {
            throw new InvalidArgumentException('texts.text_no_more_products must be a string');
        }

        if (isset($data['text_empty_cart']) && ! is_string($data['text_empty_cart'])) {
            throw new InvalidArgumentException('texts.text_empty_cart must be a string');
        }

        if (isset($data['text_order_created_success']) && ! is_string($data['text_order_created_success'])) {
            throw new InvalidArgumentException('texts.text_order_created_success must be a string');
        }

        if (isset($data['text_manager_button']) && ! is_string($data['text_manager_button'])) {
            throw new InvalidArgumentException('texts.text_manager_button must be a string');
        }
    }

    private function deserializeLogs(array $logs): LogsDTO
    {
        return new LogsDTO(
            $logs['path'],
        );
    }

    private function deserializeDatabase(array $data): DatabaseDTO
    {
        return new DatabaseDTO(
            $data['host'] ?? '',
            $data['database'] ?? '',
            $data['username'] ?? '',
            $data['password'] ?? '',
            $data['prefix'] ?? '',
            $data['port'] ?? 3306
        );
    }

    private function validateDatabase(array $data): void
    {
        if (isset($data['host']) && ! is_string($data['host'])) {
            throw new InvalidArgumentException('database.host must be a string');
        }

        if (isset($data['database']) && ! is_string($data['database'])) {
            throw new InvalidArgumentException('database.database must be a string');
        }

        if (isset($data['username']) && ! is_string($data['username'])) {
            throw new InvalidArgumentException('database.username must be a string');
        }

        if (isset($data['password']) && ! is_string($data['password'])) {
            throw new InvalidArgumentException('database.password must be a string');
        }

        if (isset($data['prefix']) && ! is_string($data['prefix'])) {
            throw new InvalidArgumentException('database.prefix must be a string');
        }

        if (isset($data['port'])) {
            if (is_string($data['port']) && ctype_digit($data['port'])) {
                $data['port'] = (int) $data['port'];
            }

            if (! is_numeric($data['port'])) {
                throw new InvalidArgumentException('database.port must be an integer');
            }
            if ($data['port'] <= 0 || $data['port'] > 65535) {
                throw new InvalidArgumentException('database.port must be between 1 and 65535');
            }
        }
    }

    private function validateLogs(array $logs): void
    {
        if (! isset($logs['path'])) {
            throw new InvalidArgumentException('Logs path must be set');
        }
    }
}
