<?php

namespace WPSN\Modules\Robots;

defined('ABSPATH') || exit;

/**
 * Helpers for structured robots.txt rules managed by WP SEO Next.
 *
 * We intentionally store these rules outside the free-form robots_txt.custom field
 * to avoid "robots.txt custom" becoming an unmaintainable dump when other modules
 * (Site Crawl / Crawl Budget) add one-click rules.
 */
final class RobotsDisallowRules
{
    /**
     * Normalize an Allow/Disallow line.
     */
    public static function normalizeLine(string $line): string
    {
        $line = trim((string) $line);
        $line = (string) preg_replace('/\s+/', ' ', $line);
        $line = trim($line);

        if ($line === '') {
            return '';
        }

        $low = strtolower($line);
        if (strpos($low, 'disallow:') === 0) {
            $val = trim(substr($line, strlen('disallow:')));
            if ($val === '') {
                return '';
            }
            return 'Disallow: ' . $val;
        }

        if (strpos($low, 'allow:') === 0) {
            $val = trim(substr($line, strlen('allow:')));
            if ($val === '') {
                return '';
            }
            return 'Allow: ' . $val;
        }

        return '';
    }

    /**
     * @param array<string,mixed> $all SettingsRepository::getAll()
     * @return array<int,array<string,mixed>>
     */
    public static function get(array $all): array
    {
        $rt = isset($all['robots_txt']) && is_array($all['robots_txt']) ? $all['robots_txt'] : [];
        $rules = isset($rt['disallow_rules']) && is_array($rt['disallow_rules']) ? $rt['disallow_rules'] : [];
        return array_values(array_filter($rules, static fn($r) => is_array($r)));
    }

    /**
     * Add one or more rules to settings array (in-place).
     *
     * @param array<string,mixed> $all
     * @param array<int,string> $lines
     * @return int number of new rules added
     */
    public static function addLinesToAll(array &$all, array $lines, string $source = '', string $note = ''): int
    {
        if (!isset($all['robots_txt']) || !is_array($all['robots_txt'])) {
            $all['robots_txt'] = [];
        }
        if (!isset($all['robots_txt']['disallow_rules']) || !is_array($all['robots_txt']['disallow_rules'])) {
            $all['robots_txt']['disallow_rules'] = [];
        }

        $rules = $all['robots_txt']['disallow_rules'];
        $rules = is_array($rules) ? array_values(array_filter($rules, static fn($r) => is_array($r))) : [];

        $existing = [];
        foreach ($rules as $r) {
            $ln = isset($r['line']) ? self::normalizeLine((string) $r['line']) : '';
            if ($ln !== '') {
                $existing[strtolower($ln)] = true;
            }
        }

        // Also dedupe against robots_txt.custom (best-effort) to avoid double entries.
        $custom = isset($all['robots_txt']['custom']) ? (string) $all['robots_txt']['custom'] : '';
        if (is_string($custom) && $custom !== '') {
            $customLines = preg_split('/\r\n|\r|\n/', $custom);
            if (is_array($customLines)) {
                foreach ($customLines as $cl) {
                    $cl = self::normalizeLine((string) $cl);
                    if ($cl !== '') {
                        $existing[strtolower($cl)] = true;
                    }
                }
            }
        }

        $added = 0;
        $source = sanitize_key($source);
        $note = sanitize_text_field((string) $note);

        foreach ($lines as $line) {
            $norm = self::normalizeLine((string) $line);
            if ($norm === '') {
                continue;
            }

            $k = strtolower($norm);
            if (isset($existing[$k])) {
                continue;
            }
            $existing[$k] = true;

            $rules[] = [
                'id' => function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : (string) uniqid('wpsn_', true),
                'enabled' => true,
                'line' => $norm,
                'source' => $source,
                'note' => $note,
                'created_at' => function_exists('current_time') ? (string) current_time('mysql', true) : gmdate('Y-m-d H:i:s'),
            ];
            $added++;

            // Prevent runaway growth.
            if (count($rules) >= 500) {
                break;
            }
        }

        $all['robots_txt']['disallow_rules'] = $rules;
        return $added;
    }

    /**
     * Toggle a rule (enabled/disabled).
     *
     * @param array<string,mixed> $all
     */
    public static function toggle(array &$all, string $id, bool $enabled): bool
    {
        $id = sanitize_text_field((string) $id);
        if ($id === '') {
            return false;
        }
        if (!isset($all['robots_txt']['disallow_rules']) || !is_array($all['robots_txt']['disallow_rules'])) {
            return false;
        }

        $changed = false;
        foreach ($all['robots_txt']['disallow_rules'] as &$r) {
            if (!is_array($r)) {
                continue;
            }
            if ((string) ($r['id'] ?? '') === $id) {
                $r['enabled'] = $enabled;
                $changed = true;
                break;
            }
        }
        unset($r);

        return $changed;
    }

    /**
     * Delete a rule.
     *
     * @param array<string,mixed> $all
     */
    public static function delete(array &$all, string $id): bool
    {
        $id = sanitize_text_field((string) $id);
        if ($id === '') {
            return false;
        }
        if (!isset($all['robots_txt']['disallow_rules']) || !is_array($all['robots_txt']['disallow_rules'])) {
            return false;
        }

        $before = count($all['robots_txt']['disallow_rules']);
        $all['robots_txt']['disallow_rules'] = array_values(array_filter(
            $all['robots_txt']['disallow_rules'],
            static fn($r) => !(is_array($r) && (string) ($r['id'] ?? '') === $id)
        ));
        return count($all['robots_txt']['disallow_rules']) !== $before;
    }
}
