<?php
namespace EasyChat\Library\Commerce;

use EasyChat\Library\SafeRuntime;






final class CommerceContextCache
{
    private const FRESH_TTL = 300;
    private const STALE_TTL = 900;

    public function get(string $key, bool $allow_stale = false, ?int $ttl_override = null): array
    {
        $path = $this->path($key);
        if ($path === '' || !is_file($path)) {
            return array();
        }
        $raw = SafeRuntime::readFile($path);
        $payload = is_string($raw) ? json_decode($raw, true) : null;
        if (!is_array($payload) || !is_array($payload['value'] ?? null)) {
            return array();
        }
        $age = time() - (int)($payload['stored_at'] ?? 0);
        $ttl = $ttl_override !== null ? max(1, $ttl_override) : ($allow_stale ? self::STALE_TTL : self::FRESH_TTL);
        if ($age < 0 || $age > $ttl) {
            return array();
        }
        $payload['cache_age'] = max(0, $age);
        $payload['cache_stale'] = $age > self::FRESH_TTL;
        return $payload;
    }

    public function put(string $key, array $value): bool
    {
        $dir = $this->directory();
        if ($dir === '' || !is_dir($dir) && !@mkdir($dir, 0700, true)) {
            return false;
        }
        $path = $this->path($key);
        $tmp = $path . '.tmp.' . bin2hex(random_bytes(4));
        $json = json_encode(array('stored_at' => time(), 'value' => $value), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if (!is_string($json) || @file_put_contents($tmp, $json, LOCK_EX) === false) {
            @unlink($tmp);
            return false;
        }
        @chmod($tmp, 0600);
        if (!@rename($tmp, $path)) {
            @unlink($tmp);
            return false;
        }
        return true;
    }

    public function trackingKey(string $provider, string $number, string $credential_fingerprint = ''): string
    {
         
         
        return hash('sha256', 'tracking|' . strtolower(trim($provider)) . '|' . strtoupper(trim($number)) . '|' . $credential_fingerprint);
    }

    public function getTracking(string $key, bool $allow_stale = false): array
    {
        $path = $this->trackingPath($key);
        if ($path === '' || !is_file($path)) return array();
        $raw = SafeRuntime::readFile($path);
        $payload = is_string($raw) ? json_decode($raw, true) : null;
        if (!is_array($payload) || !is_array($payload['value'] ?? null)) return array();
        $ttl = (int)($payload['ttl'] ?? 60);
        $age = time() - (int)($payload['stored_at'] ?? 0);
        if ($age < 0 || $age > ($allow_stale ? max($ttl, 900) : $ttl)) return array();
        $payload['cache_age'] = max(0, $age);
        return $payload;
    }

    public function putTracking(string $key, array $shipment, int $ttl): bool
    {
        $dir = $this->directory();
        if ($dir === '' || (!is_dir($dir) && !@mkdir($dir, 0700, true))) return false;
        $path = $this->trackingPath($key);
        $tmp = $path . '.tmp.' . bin2hex(random_bytes(4));
        $json = json_encode(array('stored_at' => time(), 'ttl' => max(30, $ttl), 'value' => array('shipment' => $shipment)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if (!is_string($json) || @file_put_contents($tmp, $json, LOCK_EX) === false) { @unlink($tmp); return false; }
        @chmod($tmp, 0600);
        if (!@rename($tmp, $path)) { @unlink($tmp); return false; }
        return true;
    }

    public function claimTracking(string $key, int $lease_seconds = 45): bool
    {
        $dir = $this->directory();
        if ($dir === '' || (!is_dir($dir) && !@mkdir($dir, 0700, true))) return false;
        $path = $dir . DIRECTORY_SEPARATOR . 'track_' . $key . '.lock';
        $handle = @fopen($path, 'c+');
        if (!$handle) return false;
        $deadline = microtime(true) + min(2, max(1, $lease_seconds));
        do {
            if (@flock($handle, LOCK_EX | LOCK_NB)) {
                $raw = stream_get_contents($handle);
                $last = is_string($raw) ? (int)trim($raw) : 0;
                if ($last > 0 && time() - $last < max(5, $lease_seconds)) {
                    @flock($handle, LOCK_UN); @fclose($handle); return false;
                }
                rewind($handle); @ftruncate($handle, 0); @fwrite($handle, (string)time()); @fflush($handle);
                @flock($handle, LOCK_UN); @fclose($handle); return true;
            }
            usleep(100000);
            $cached = $this->getTracking($key, true);
            if ($cached !== array()) { @fclose($handle); return false; }
        } while (microtime(true) < $deadline);
        @fclose($handle);
        return false;
    }

     
    public function releaseTracking(string $key): void
    {
        $dir = $this->directory();
        $path = $dir === '' ? '' : $dir . DIRECTORY_SEPARATOR . 'track_' . $key . '.lock';
        if ($path === '' || !is_file($path)) return;
        $handle = @fopen($path, 'c+');
        if (!$handle || !@flock($handle, LOCK_EX)) { if ($handle) @fclose($handle); return; }
        @ftruncate($handle, 0); @fflush($handle); @flock($handle, LOCK_UN); @fclose($handle);
    }

     
    public function claimRefresh(string $key, int $cooldown = 15): bool
    {
        $dir = $this->directory();
        if ($dir === '' || (!is_dir($dir) && !@mkdir($dir, 0700, true))) return false;
        $path = $dir . DIRECTORY_SEPARATOR . $key . '.refresh';
        $handle = @fopen($path, 'c+');
        if (!$handle || !@flock($handle, LOCK_EX)) { if ($handle) @fclose($handle); return false; }
        $raw = stream_get_contents($handle);
        $last = is_string($raw) ? (int)trim($raw) : 0;
        if ($last > 0 && time() - $last < max(5, $cooldown)) {
            @flock($handle, LOCK_UN); @fclose($handle); return false;
        }
        rewind($handle); @ftruncate($handle, 0); @fwrite($handle, (string)time()); @fflush($handle);
        @flock($handle, LOCK_UN); @fclose($handle); @chmod($path, 0600);
        return true;
    }

    public function key(int $customer_id, array $lookup, int $limit, array $settings): string
    {
        $fingerprint = array(
            'customer' => $customer_id,
            'emails' => array_values(array_unique(array_map('strtolower', (array)($lookup['emails'] ?? array())))),
            'phones' => array_values(array_unique((array)($lookup['phones'] ?? array()))),
            'limit' => $limit,
            'cms' => (string)($settings['easychat_cms_edition'] ?? (defined('EASYCHAT_CMS_EDITION') ? EASYCHAT_CMS_EDITION : '')),
            'moysklad' => (string)($settings['chat_moysklad_enabled'] ?? '0'),
             
            'moysklad_token' => hash('sha256', (string)($settings['chat_moysklad_token'] ?? '')),
            'moysklad_mapping' => hash('sha256', (string)($settings['chat_moysklad_field_mapping'] ?? '')),
        );
        return hash('sha256', json_encode($fingerprint, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
    }

    private function directory(): string
    {
        $base = defined('DIR_STORAGE') ? (string)DIR_STORAGE : (defined('DIR_LOGS') ? dirname((string)DIR_LOGS) : '');
        return $base === '' ? '' : rtrim($base, '/\\') . DIRECTORY_SEPARATOR . 'easychat-private' . DIRECTORY_SEPARATOR . 'commerce-cache';
    }

    private function path(string $key): string
    {
        if (!preg_match('/^[a-f0-9]{64}$/', $key)) return '';
        $dir = $this->directory();
        return $dir === '' ? '' : $dir . DIRECTORY_SEPARATOR . $key . '.json';
    }

    private function trackingPath(string $key): string
    {
        if (!preg_match('/^[a-f0-9]{64}$/', $key)) return '';
        $dir = $this->directory();
        return $dir === '' ? '' : $dir . DIRECTORY_SEPARATOR . 'track_' . $key . '.json';
    }
}
