<?php

namespace Bastion\Handlers;

use Bastion\Exceptions\BotTokenConfiguratorException;
use Bastion\Services\BotTokenConfigurator;
use Bastion\Services\SettingsService;
use Exception;
use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\Config\Settings;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\Support\Arr;
use Psr\Log\LoggerInterface;

class SettingsHandler
{
    private BotTokenConfigurator $botTokenConfigurator;
    private Settings $settings;
    private SettingsService $settingsUpdateService;
    private CacheInterface $cache;
    private LoggerInterface $logger;
    private Builder $builder;
    private ConnectionInterface $connection;

    public function __construct(
        BotTokenConfigurator $botTokenConfigurator,
        Settings $settings,
        SettingsService $settingsUpdateService,
        CacheInterface $cache,
        LoggerInterface $logger,
        Builder $builder,
        ConnectionInterface $connection
    ) {
        $this->botTokenConfigurator = $botTokenConfigurator;
        $this->settings = $settings;
        $this->settingsUpdateService = $settingsUpdateService;
        $this->cache = $cache;
        $this->logger = $logger;
        $this->builder = $builder;
        $this->connection = $connection;
    }

    public function configureBotToken(Request $request): JsonResponse
    {
        try {
            $data = $this->botTokenConfigurator->configure(trim($request->json('botToken', '')));

            return new JsonResponse($data);
        } catch (BotTokenConfiguratorException $e) {
            return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
        } catch (Exception $e) {
            return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function getSettingsForm(): JsonResponse
    {
        $data = Arr::getWithKeys($this->settings->getAll(), [
            'app',
            'telegram',
            'metrics',
            'store',
            'orders',
            'texts',
            'sliders',
            'mainpage_blocks',
            'pulse',
            'cron',
        ]);

        if (!isset($data['cron']['mode'])) {
            $data['cron']['mode'] = 'disabled';
        }

        $data['forms'] = [];

        // Add CRON system details (read-only)
        $data['cron']['cli_path'] = BP_REAL_BASE_PATH . '/cli.php';
        $data['cron']['last_run'] = $this->getLastCronRunDate();

        $forms = $this->builder->newQuery()
            ->from('telecart_forms')
            ->get();

        if ($forms) {
            foreach ($forms as $form) {
                try {
                    $schema = json_decode($form['schema'] ?? '[]', true, 512, JSON_THROW_ON_ERROR);
                } catch (\JsonException $exception) {
                    $schema = [];
                }

                $data['forms'][$form['alias']] = [
                    'alias' => $form['alias'],
                    'friendly_name' => $form['friendly_name'],
                    'is_custom' => filter_var($form['is_custom'], FILTER_VALIDATE_BOOLEAN),
                    'schema' => $schema,
                ];
            }
        }

        return new JsonResponse(compact('data'));
    }

    public function saveSettingsForm(Request $request): JsonResponse
    {
        $input = $request->json();

        $this->validate($input);

        // Remove dynamic properties before saving
        if (isset($input['cron'])) {
            unset($input['cron']['cli_path']);
            unset($input['cron']['last_run']);
        }

        $this->settingsUpdateService->update(
            Arr::getWithKeys($input, [
                'app',
                'telegram',
                'metrics',
                'store',
                'orders',
                'texts',
                'sliders',
                'mainpage_blocks',
                'pulse',
                'cron',
            ]),
        );

        // Update forms
        $forms = Arr::get($input, 'forms', []);
        foreach ($forms as $form) {
            $schema = json_encode($form['schema'], JSON_THROW_ON_ERROR);
            $this->builder->newQuery()
                ->where('alias', '=', $form['alias'])
                ->update('telecart_forms', [
                    'friendly_name' => $form['friendly_name'],
                    'is_custom' => $form['is_custom'],
                    'schema' => $schema,
                ]);
        }

        return new JsonResponse([], Response::HTTP_ACCEPTED);
    }

    private function validate(array $input): void
    {
    }

    public function resetCache(): JsonResponse
    {
        $this->cache->clear();

        $this->logger->info('Cache cleared manually.');

        return new JsonResponse([], Response::HTTP_ACCEPTED);
    }

    private function getLastCronRunDate(): ?string
    {
        try {
            // Since we are in SettingsHandler, we already have access to container or we can inject SchedulerService
            // But SettingsHandler is constructed via DI. Let's add SchedulerService to constructor.
            // For now, let's use global retrieval via cache if possible, or assume it's injected.
            // But wait, getLastCronRunDate logic was in controller.
            // SchedulerService stores last run in cache. We have $this->cache here.

            $lastRunTimestamp = $this->cache->get("scheduler.global_last_run");

            if ($lastRunTimestamp) {
                return date('d.m.Y H:i:s', (int)$lastRunTimestamp);
            }

            return null;
        } catch (Exception $e) {
            return null;
        }
    }

    public function getSystemInfo(): JsonResponse
    {
        $info = [];

        $info['PHP Version'] = PHP_VERSION;
        $info['PHP SAPI'] = PHP_SAPI;
        $info['PHP Memory Limit'] = ini_get('memory_limit');
        $info['PHP Memory Usage'] = $this->formatBytes(memory_get_usage(true));
        $info['PHP Peak Memory Usage'] = $this->formatBytes(memory_get_peak_usage(true));
        $info['PHP Max Execution Time'] = ini_get('max_execution_time') . 's';
        $info['PHP Upload Max Filesize'] = ini_get('upload_max_filesize');
        $info['PHP Post Max Size'] = ini_get('post_max_size');

        try {
            $mysqlVersion = $this->connection->select('SELECT VERSION() as version');
            $info['MySQL Version'] = $mysqlVersion[0]['version'] ?? 'Unknown';
        } catch (Exception $e) {
            $info['MySQL Version'] = 'Error: ' . $e->getMessage();
        }

        $cacheDriver = env('TELECART_CACHE_DRIVER', 'mysql');
        $cacheClass = get_class($this->cache);
        $info['Cache Driver'] = $cacheDriver . ' (' . basename(str_replace('\\', '/', $cacheClass)) . ')';

        $info['Module Version'] = module_version();
        $info['OpenCart Version'] = defined('VERSION') ? VERSION : 'Unknown';
        $info['OpenCart Core Version'] = defined('VERSION_CORE') ? VERSION_CORE : 'Unknown';

        $info['Operating System'] = PHP_OS;
        $info['Server Software'] = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
        $info['Document Root'] = $_SERVER['DOCUMENT_ROOT'] ?? 'Unknown';

        $info['PHP Timezone'] = date_default_timezone_get();
        $info['Server Time'] = date('Y-m-d H:i:s');
        $info['UTC Time'] = gmdate('Y-m-d H:i:s');

        $info['Loaded PHP Extensions'] = implode(', ', get_loaded_extensions());

        $infoText = '';
        foreach ($info as $key => $value) {
            $infoText .= $key . ': ' . $value . "\n";
        }

        return new JsonResponse(['data' => $infoText]);
    }

    private function formatBytes(int $bytes, int $precision = 2): string
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];

        for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
            $bytes /= 1024;
        }

        return round($bytes, $precision) . ' ' . $units[$i];
    }
}
