<?php

namespace ZenoChatbot\Services\ContextCache;

use ZenoChatbot\Helper\Resources;
use ZenoChatbot\Support\Hooks;
use ZenoChatbot\Support\ResourceFormatter;

if (!defined('ABSPATH')) {
    exit;
}

class ResourceTextCache
{
    private const META_SUFFIX = '.meta.json';
    private const CACHE_SUFFIX = '.text.cache';
    private const DEFAULT_TTL_SECONDS = 21600; // 6 hours

    /**
     * Build or reuse formatted text cache for a JSON resource.
     *
     * @param string $resourceName
     * @param string $rawJson
     * @return string
     */
    public static function getOrBuild(string $resourceName, string $rawJson): string
    {
        $fingerprint = hash('sha256', $rawJson);
        $meta = static::readMeta($resourceName);
        $cacheFile = static::getCacheFilePath($resourceName);

        if (
            is_array($meta)
            && ($meta['fingerprint'] ?? '') === $fingerprint
            && file_exists($cacheFile)
            && !static::isExpired($meta)
        ) {
            $cached = file_get_contents($cacheFile);
            if ($cached !== false) {
                return (string) $cached;
            }
        }

        $text = ResourceFormatter::fromJson($rawJson);
        static::writeWithLock($cacheFile, $text);
        static::writeMeta($resourceName, [
            'fingerprint' => $fingerprint,
            'generated_at' => gmdate('c'),
        ]);

        return $text;
    }

    /**
     * Invalidate a cached resource.
     *
     * @param string $resourceName
     * @return void
     */
    public static function invalidate(string $resourceName): void
    {
        $cacheFile = static::getCacheFilePath($resourceName);
        $metaFile = static::getMetaFilePath($resourceName);

        if (file_exists($cacheFile)) {
            @wp_delete_file($cacheFile);
        }

        if (file_exists($metaFile)) {
            @wp_delete_file($metaFile);
        }
    }

    /**
     * Invalidate all known default resource caches.
     *
     * @return void
     */
    public static function invalidateDefaults(): void
    {
        static::invalidate('internal-content-index.json');
        static::invalidate('external-sources.json');
        static::invalidate('knowledge-notes-index.json');
        static::invalidate('sitemap-content-index.json');
        static::invalidate('product-content-index.json');
    }

    /**
     * @param string $resourceName
     * @return string
     */
    private static function getCacheFilePath(string $resourceName): string
    {
        $base = static::normalizeResourceName($resourceName);
        return Resources::getCacheDirPath() . '/' . $base . static::CACHE_SUFFIX;
    }

    /**
     * @param string $resourceName
     * @return string
     */
    private static function getMetaFilePath(string $resourceName): string
    {
        $base = static::normalizeResourceName($resourceName);
        return Resources::getCacheDirPath() . '/' . $base . static::META_SUFFIX;
    }

    /**
     * @param string $resourceName
     * @return array|null
     */
    private static function readMeta(string $resourceName): ?array
    {
        $path = static::getMetaFilePath($resourceName);
        if (!file_exists($path)) {
            return null;
        }

        $content = file_get_contents($path);
        if ($content === false) {
            return null;
        }

        $decoded = json_decode($content, true);
        return is_array($decoded) ? $decoded : null;
    }

    /**
     * @param string $resourceName
     * @param array<string,string> $meta
     * @return void
     */
    private static function writeMeta(string $resourceName, array $meta): void
    {
        static::writeWithLock(
            static::getMetaFilePath($resourceName),
            (string) json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
        );
    }

    /**
     * @param array<string,mixed> $meta
     * @return bool
     */
    private static function isExpired(array $meta): bool
    {
        $generatedAt = (string) ($meta['generated_at'] ?? '');
        if ($generatedAt === '') {
            return true;
        }

        $timestamp = strtotime($generatedAt);
        if ($timestamp === false) {
            return true;
        }

        $ttl = (int) Hooks::applyFilters(Hooks::CACHE_RESOURCE_TTL, static::DEFAULT_TTL_SECONDS);
        if ($ttl <= 0) {
            return false;
        }

        return (time() - $timestamp) > $ttl;
    }

    /**
     * @param string $resourceName
     * @return string
     */
    private static function normalizeResourceName(string $resourceName): string
    {
        return preg_replace('/[^a-zA-Z0-9._-]/', '_', $resourceName) ?? $resourceName;
    }

    /**
     * @param string $path
     * @param string $content
     * @return void
     */
    private static function writeWithLock(string $path, string $content): void
    {
        $dir = dirname($path);
        if (!file_exists($dir)) {
            wp_mkdir_p($dir);
        }

        file_put_contents($path, $content, LOCK_EX);
    }
}
