<?php

namespace intec\template\actions;

use Exception;
use Bitrix\Main\Localization\Loc;
use intec\core\helpers\ArrayHelper;
use intec\core\helpers\Type;
use intec\template\Template;
use Psr\Log\InvalidArgumentException;

Loc::loadMessages(__FILE__);

class SettingsActions extends BaseActions
{
    private function getSessionKey($id)
    {
        return md5('settings-' . $id);
    }

    private function exists($id)
    {
        return ArrayHelper::getValue($_SESSION, ['settings', $this->getSessionKey($id)], false);
    }

    public function actionGet(string $source = ''): array
    {
        $root = realpath($_SERVER['DOCUMENT_ROOT']);
        $source = trim($source);

        if ($root === false) {
            return $this->errorResponse('Server configuration error');
        }

        if ($source === '') {
            $path = '/include/kosmos/.settings.json';
        } else {
            $path = $source;
        }

        $relative = ltrim(
            string: str_replace(search: '\\', replace: '/', subject: $path),
            characters: '/'
        );
        $candidate = $root . DIRECTORY_SEPARATOR . str_replace(
            search: '/',
            replace: DIRECTORY_SEPARATOR,
            subject: $relative
        );
        $resolved = realpath($candidate);

        if (
            $resolved === false ||
            (!str_starts_with($resolved, $root . DIRECTORY_SEPARATOR) && $resolved !== $root)
        ) {
            return $this->errorResponse('Unsafe file path');
        }

        if (!is_file($resolved)) {
            return $this->errorResponse('Not a file');
        }

        if (!is_readable($resolved)) {
            return $this->errorResponse('File is not readable');
        }

        $content = file_get_contents($resolved);

        if ($content === false) {
            return $this->errorResponse('Failed to read file');
        }

        if ($content === '') {
            return $this->errorResponse('File is empty');
        }

        $content = mb_convert_encoding(
            string: $content,
            to_encoding: 'UTF-8',
            from_encoding: 'auto'
        );

        try {
            $response = json_decode(
                json: $content,
                associative: true,
                flags: JSON_THROW_ON_ERROR
            );
        } catch (Exception) {
            return $this->errorResponse('Invalid JSON');
        }

        return $this->successResponse($response);
    }

    public function actionSet($menu = [], $asDefault = false)
    {
        $response = [];
        $template = Template::getInstance();
        $newSettings = [];

        $newSettings = self::formatData($menu);

        if (!$asDefault)
            $response['success'] = $template->settings->setData($newSettings);
        else
            $response['success'] = $template->settings->setNewDefaultData($newSettings);

        if (!$asDefault && !$response['success'])
            $template->settings->setDefaultData();

        return $this->successResponse($response);
    }

    public function actionExport($menu = [])
    {
        $response = [];
        $template = Template::getInstance();

        $response['data'] = self::formatData($menu);
        $response['data']['solution'] = 'intec.digital2';

        return $this->successResponse($response);
    }

    public function actionImport($data)
    {
        $response = [];
        $template = Template::getInstance();

        if ($this->importValidate($data['data'])) {
            $response['success'] = $template->settings->setData($data['data']);
        } else {
            $response['success'] = false;
        }


        return $this->successResponse($response);
    }

    public function importValidate($data)
    {
        if ($data['solution'] !== 'intec.digital2')
            return false;

        $valid = true;

        $available = ['solution', 'ready_variants', 'base', 'main', 'header', 'topMenu', 'catalog', 'services', 'projects', 'sections', 'basket', 'footer', 'cabinet', 'mobile'];

        foreach ($data as $key => $field) {
            if (!in_array($key, $available)) {
                $valid = false;
                break;
            }
        }

        try {
            self::sanitizeJson($data);
        } catch (Exception $e) {
            $valid = false;
        }

        return $valid;
    }

    private function sanitizeJson($data)
    {
        if (is_array($data)) {
            return array_map([$this, 'sanitizeJson'], $data);
        }

        if (is_string($data)) {
            if (self::hasInjectionAttempts($data)) {
                throw new InvalidArgumentException("External injection attempt");
            }
            return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
        }

        return $data;
    }

    private function hasInjectionAttempts($value)
    {
        $patterns = [
            '/<script/i',
            '/javascript:/i',
            '/on[a-z]+\s*=/i',
            '/<\?(php|=)?/i',
            '/\b(select|insert|update|delete|drop|union)\b/i'
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $value)) {
                return true;
            }
        }
        return false;
    }

    public function actionDefault($menu = [])
    {
        $response = [];
        $template = Template::getInstance();

        $response['success'] = $template->settings->setDefaultData();

        return $this->successResponse($response);
    }

    public function actionVariant($variant, $source)
    {
        $response = [];
        $template = Template::getInstance();

        if (strlen($source)) {
            $response['success'] = $template->settings->setVariantData($variant, $source);
        }

        return $this->successResponse($response);
    }

    public function bindActionParameters($action, $parameters)
    {
        if ($action->getId() === 'get')
            return [
                'source' => $parameters['source']
            ];

        return parent::bindActionParameters($action, $parameters);
    }

    public function actionInclude ($path)
    {
        //return $path;
        //return $this->successResponse();
        //return include_once ($path);
    }

    private function formatData($menu):array
    {
        $newSettings = [];

        foreach ($menu['variants']['values'] as $group) {
            foreach ($group as $tab) {
                foreach ($tab['variants'] as $section) {
                    foreach ($section['variants'] as $variantKey => $variant) {
                        if ($variantKey === 'null')
                            $variantKey = null;

                        if ($variant['active']) {
                            $newSettings[$section['code']] = $variantKey;
                        }
                    }
                }
            }
        }

        foreach ($menu['settings']['values'] as $groupKey => $group) {
            foreach ($group as $tab) {
                foreach ($tab['variants'] as $sectionKey => $section) {
                    if ($section['type'] === 'switch') {
                        $newSettings[$groupKey][$section['code']]['template'] = $section['active'];
                        $newSettings[$groupKey][$section['code']]['sort'] = $sectionKey + 1;
                    } elseif ($section['type'] === 'color' && $section['custom']['active']) {
                        $newSettings[$groupKey][$section['code']]['template'] = $section['custom']['value'];
                        $newSettings[$groupKey][$section['code']]['sort'] = $sectionKey + 1;

                        if (ArrayHelper::keyExists('params', $section) && ArrayHelper::keyExists('show', $section['params']))
                            $newSettings[$groupKey][$section['code']]['show'] = $section['params']['show']['value'];

                    } elseif (!empty($section['variants'])) {
                        foreach ($section['variants'] as $variantKey => $variant) {
                            if ($variantKey === 'null')
                                $variantKey = null;

                            if ($variant['active'] || $variant['type'] === 'switch') {
                                $newSettings[$groupKey][$section['code']]['template'] = $variantKey;
                                $newSettings[$groupKey][$section['code']]['sort'] = $sectionKey + 1;

                                if (!empty($variant['params'])) {
                                    foreach ($variant['params'] as $param) {
                                        $newSettings[$groupKey][$section['code']]['params'][$param['code']] = $param['active'];
                                    }
                                }

                                if (Type::isArray($section) && ArrayHelper::keyExists('params', $section) && Type::isArray($section['params']) && ArrayHelper::keyExists('show', $section['params']))
                                    $newSettings[$groupKey][$section['code']]['show'] = $section['params']['show']['value'];
                            }
                        }
                    }

                    if ($section['type'] === 'roll') {
                        if(!empty($newSettings[$groupKey][$section['code']]) && !empty($section['params'])) {
                            foreach ($section['params'] as $paramKey => $param) {
                                if (Type::isArray($param) && ArrayHelper::keyExists('value', $param))
                                    $newSettings[$groupKey][$section['code']]['params'][$paramKey] = $param['value'];
                                else
                                    $newSettings[$groupKey][$section['code']]['params'][$paramKey] = $param;

                            }
                        }
                    }
                }

                if (!empty($newSettings[$groupKey]))
                    uasort($newSettings[$groupKey], function($a, $b) {
                        return ($a['sort'] - $b['sort']);
                    });
            }
        }

        return $newSettings;
    }
}
