<?php

declare(strict_types=1);

namespace ShieldX\Maintenance;

defined('ABSPATH') || exit;

final class LogRotationSettings
{
    private const SETTINGS_OPTION = 'shieldx_log_rotation_settings';
    private const LAST_RESULT_OPTION = 'shieldx_log_rotation_last_result';
    private const LAST_FILE_SCAN_OPTION = 'shieldx_log_rotation_last_file_scan';
    private const LAST_TEMP_CLEANUP_OPTION = 'shieldx_temp_cleanup_last_result';

    public static function fromWordPress(): self
    {
        return new self();
    }

    /**
     * @return array<string,mixed>
     */
    public function all(): array
    {
        $stored = function_exists('get_option') ? get_option(self::SETTINGS_OPTION, []) : [];
        $stored = is_array($stored) ? $stored : [];

        return [
            'enabled' => !array_key_exists('enabled', $stored) || !empty($stored['enabled']),
            'retention_days' => max(1, min(3650, (int) ($stored['retention_days'] ?? 90))),
            'max_files' => max(1, min(7300, (int) ($stored['max_files'] ?? 90))),
            'targets' => array_key_exists('targets', $stored) && is_array($stored['targets'])
                ? self::cleanTargets($stored['targets'])
                : array_keys(LogRotator::targetCatalog()),
            'excluded_file_logs' => array_key_exists('excluded_file_logs', $stored) && is_array($stored['excluded_file_logs'])
                ? self::cleanFileLogNames($stored['excluded_file_logs'])
                : [],
        ];
    }

    /**
     * @param array<string,mixed> $settings
     */
    public function save(array $settings): void
    {
        $payload = [
            'enabled' => !empty($settings['enabled']),
            'retention_days' => max(1, min(3650, (int) ($settings['retention_days'] ?? 90))),
            'max_files' => max(1, min(7300, (int) ($settings['max_files'] ?? 90))),
            'targets' => self::cleanTargets(isset($settings['targets']) && is_array($settings['targets']) ? $settings['targets'] : []),
            'excluded_file_logs' => self::cleanFileLogNames(isset($settings['excluded_file_logs']) && is_array($settings['excluded_file_logs']) ? $settings['excluded_file_logs'] : []),
        ];

        if (function_exists('update_option')) {
            update_option(self::SETTINGS_OPTION, $payload, false);
        }
    }

    /**
     * @return array<string,mixed>
     */
    public function lastResult(): array
    {
        $stored = function_exists('get_option') ? get_option(self::LAST_RESULT_OPTION, []) : [];

        return is_array($stored) ? $stored : [];
    }

    /**
     * @return array<string,mixed>
     */
    public function lastFileScan(): array
    {
        $stored = function_exists('get_option') ? get_option(self::LAST_FILE_SCAN_OPTION, []) : [];

        return is_array($stored) ? $stored : [];
    }

    /**
     * @return array<string,mixed>
     */
    public function lastTempCleanup(): array
    {
        $stored = function_exists('get_option') ? get_option(self::LAST_TEMP_CLEANUP_OPTION, []) : [];

        return is_array($stored) ? $stored : [];
    }

    /**
     * @param array<string,mixed> $result
     */
    public function recordLastResult(array $result): void
    {
        if (function_exists('update_option')) {
            update_option(self::LAST_RESULT_OPTION, $result, false);
        }
    }

    /**
     * @param array<string,mixed> $result
     */
    public function recordLastFileScan(array $result): void
    {
        if (function_exists('update_option')) {
            update_option(self::LAST_FILE_SCAN_OPTION, $result, false);
        }
    }

    /**
     * @param array<string,mixed> $result
     */
    public function recordLastTempCleanup(array $result): void
    {
        if (function_exists('update_option')) {
            try {
                update_option(self::LAST_TEMP_CLEANUP_OPTION, $result, false);
            } catch (\Throwable $error) {
                \ShieldX\Support\ErrorLog::write('SHIELD-X temporary cleanup result write failed: ' . $error->getMessage());
            }
        }
    }

    /**
     * @param array<int,mixed> $targets
     * @return array<int,string>
     */
    private static function cleanTargets(array $targets): array
    {
        $allowed = array_fill_keys(array_keys(LogRotator::targetCatalog()), true);
        $clean = [];

        foreach ($targets as $target) {
            if (!is_scalar($target)) {
                continue;
            }

            $target = (string) $target;
            if (isset($allowed[$target])) {
                $clean[$target] = $target;
            }
        }

        return array_values($clean);
    }

    /**
     * @param array<int,mixed> $files
     * @return array<int,string>
     */
    public static function cleanFileLogNames(array $files): array
    {
        $clean = [];

        foreach ($files as $file) {
            if (!is_scalar($file)) {
                continue;
            }

            $name = strtolower(trim(str_replace('\\', '/', (string) $file), "/ \t\n\r\0\x0B"));
            if ($name === '' || strpos($name, '..') !== false || !self::isSafeLogPath($name)) {
                continue;
            }

            $clean[$name] = $name;
        }

        return array_values($clean);
    }

    private static function isSafeLogPath(string $path): bool
    {
        $segments = explode('/', $path);
        $last = end($segments);
        if (!is_string($last)) {
            return false;
        }

        foreach ($segments as $segment) {
            if ($segment === '' || $segment === '.' || $segment === '..' || !preg_match('/^[A-Za-z0-9._-]+$/', $segment)) {
                return false;
            }
        }

        $last = strtolower($last);
        if (self::isDocumentationFile($last)) {
            return false;
        }

        if ($last === 'error_log' || $last === 'php_errorlog') {
            return true;
        }

        if (preg_match('/\.(?:log(?:\.[A-Za-z0-9_-]+)?|jsonl|ndjson)$/', $last)) {
            return true;
        }

        return self::pathHasLogDirectory($segments) && preg_match('/\.(?:txt|json|csv)$/', $last) === 1;
    }

    /**
     * @param array<int,string> $segments
     */
    private static function pathHasLogDirectory(array $segments): bool
    {
        foreach ($segments as $segment) {
            $segment = strtolower($segment);
            if ($segment === 'log' || $segment === 'logs' || $segment === 'debug' || $segment === 'debugs' || substr($segment, -4) === 'logs' || substr($segment, -5) === '-logs' || substr($segment, -5) === '_logs') {
                return true;
            }
        }

        return false;
    }

    private static function isDocumentationFile(string $name): bool
    {
        return preg_match('/^(?:change(?:log)?|changelog|readme|license|licence|copying|credits)(?:[-_.].*)?\.(?:txt|md|html?)$/', $name) === 1;
    }
}
