<?php

declare(strict_types=1);

namespace apexl\Io\services;

use apexl\Io\includes\HookManager;
use apexl\Vault\Vault;

final readonly class VariableManager
{
    public final const string HOOK__GET_VARIABLE = 'variable.get';

    public function __construct(
        private Vault $db,
        private InstallChecker $installChecker,
        private HookManager $hookManager,
    ) {}

    public function delete(string $name, int $site = 1): void
    {
        $this->db->delete('variables')
            ->fields()
            ->where('site', $site)
            ->where('name', $name)
            ->execute();
    }

    public function all(int $site = 1): array
    {
        //DB isn't available when it's not installed, so skip the query if we're running core install.
        $vars = [];

        if ($this->installChecker->isInstalled) {
            $result = $this->db->select('variables')
                ->fields(['name', 'value'])
                ->where('site', $site)
                ->execute()
                ->fetchAllAssoc();

            foreach ($result as $row) {
                $value = $row['value'];

                $vars[$row['name']] = json_validate($value) ? json_decode($value) : $value;

            }
        }

        return $vars;
    }

    public function get(string $name, int $site = 1): mixed
    {
        $result = null;
        //DB isn't available when it's not installed, so skip the query if we're running core installation.
        if ($this->installChecker->isInstalled) {
            $row = $this->db->select('variables')
                ->fields('value')
                ->where('site', $site)
                ->where('name', $name)
                ->limit(1)
                ->execute()
                ->fetchAssoc();

            $value = $row['value'] ?? false;

            if ($value) {
                $result = json_validate($value) ? json_decode($value) : $value;
            }
        }

        $result = $this->hookManager->processHook(self::HOOK__GET_VARIABLE, $result, $name, $site);

        return $this->hookManager->processHook(sprintf('%s:%s', self::HOOK__GET_VARIABLE, $name), $result, $site);
    }

    public function set(string $name, mixed $data, int $site = 1): void
    {
        $cleanedData = match (gettype($data)) {
            'array', 'object', 'boolean' => json_encode($data),
            default => $data,
        };
        //insert or update?
        if ($this->has($name, $site)) {
            $this->db->update('variables')->fields([
                'value' => $cleanedData,
                'site' => $site,
            ])->where('site', $site)
                ->where('name', $name)
                ->execute();
        }

        $this->db->insert('variables')->fields([
            'name' => $name,
            'value' => $cleanedData,
            'site' => $site,
        ])->execute();
    }

    public function has(string $name, int $site = 1): bool
    {
        //DB isn't available when it's not installed, so skip the query if we're running core install.
        if (!$this->installChecker->isInstalled) {
            return false;
        }
        $var = $this->db->select('variables')
            ->fields('count(*) as count')
            ->where('site', $site)
            ->where('name', $name)
            ->execute()
            ->fetchAssoc();

        return (int) $var['count'] !== 0;
    }
}