<?php

namespace Openguru\OpenCartFramework\Settings;

use Exception;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\Support\Arr;
use JsonException;
use RuntimeException;

class DatabaseUserSettings implements UserSettingsInterface
{
    private $builder;
    private $database;
    private $allSettings = [];

    public function __construct(Builder $builder, ConnectionInterface $database)
    {
        $this->builder = $builder;
        $this->database = $database;
    }

    public function getAll(): array
    {
        if ($this->allSettings) {
            return $this->allSettings;
        }

        $row = $this->builder
            ->select(['settings_json'])
            ->from(config('tables.settings'))
            ->where('id', '=', 1)
            ->firstOrNull();

        if (! $row || empty($row['settings_json'])) {
            throw new RuntimeException('Database settings not found. Please reinstall the module.');
        }

        try {
            $decoded = json_decode($row['settings_json'], true, 512, JSON_THROW_ON_ERROR);
        } catch (JsonException $e) {
            throw new RuntimeException('Database settings invalid format: ' . $e->getMessage());
        }

        $this->allSettings = $decoded;

        return $decoded;
    }

    public function update(array $userSettings): void
    {
        $settingsTable = config('tables.settings');
        try {
            $currentSettings = $this->getAll();
            $updatedSettings = array_replace_recursive($currentSettings, $userSettings);

            $encoded = json_encode($updatedSettings, JSON_THROW_ON_ERROR);

            $sql = <<<SQL
                INSERT INTO `{$settingsTable}`
                    (id, settings_json)
                VALUES
                    (1, '{$encoded}')
                ON DUPLICATE KEY UPDATE
                    settings_json = VALUES(settings_json)
            SQL;

            $this->database->statement($sql, [$encoded]);
        } catch (Exception $e) {
            throw new RuntimeException('Could not update settings: ' . $e->getMessage());
        }
    }

    public function get(string $key, $default = null)
    {
        $all = $this->getAll();

        return Arr::get($all, $key, $default);
    }

    public function safeGet(string $key, $default = null)
    {
        try {
            return $this->get($key, $default);
        } catch (Exception $e) {
            return $default;
        }
    }
}
