<?php

namespace App\Rules;

use App\Services\Billing\InvoicingRuleEvaluator;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

/**
 * Recursive validator for InvoiceBillingRule.conditions trees.
 *
 * Verifies that:
 *   - Each node has type ∈ {group, condition}
 *   - Group operators ∈ {AND, OR} and have an array of children
 *   - Condition fields ∈ ALLOWED_FIELDS whitelist
 *   - Condition operators ∈ ALLOWED_OPERATORS whitelist
 *   - Recursive depth ≤ 5 (defensive against pathological payloads)
 */
class ValidConditionTree implements ValidationRule
{
    private const MAX_DEPTH = 5;

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (! is_array($value)) {
            $fail('Conditions must be a structured object.');

            return;
        }

        $error = $this->walk($value, 0);
        if ($error !== null) {
            $fail($error);
        }
    }

    private function walk(array $node, int $depth): ?string
    {
        if ($depth > self::MAX_DEPTH) {
            return 'Conditions tree exceeds maximum depth ('.self::MAX_DEPTH.').';
        }

        $type = $node['type'] ?? null;
        if (! in_array($type, ['group', 'condition'], true)) {
            return "Each node must have type 'group' or 'condition'.";
        }

        if ($type === 'group') {
            $op = $node['operator'] ?? null;
            if (! in_array($op, ['AND', 'OR'], true)) {
                return "Group operator must be 'AND' or 'OR'.";
            }
            $children = $node['children'] ?? null;
            if (! is_array($children)) {
                return 'Group must have children array.';
            }
            foreach ($children as $child) {
                if (! is_array($child)) {
                    return 'Group children must be node objects.';
                }
                $err = $this->walk($child, $depth + 1);
                if ($err !== null) {
                    return $err;
                }
            }

            return null;
        }

        // type === 'condition'
        $field = $node['field'] ?? null;
        if (! in_array($field, InvoicingRuleEvaluator::ALLOWED_FIELDS, true)) {
            return "Unknown condition field: {$field}";
        }
        $op = $node['op'] ?? null;
        if (! in_array($op, InvoicingRuleEvaluator::ALLOWED_OPERATORS, true)) {
            return "Unsupported operator: {$op}";
        }
        if (! array_key_exists('value', $node)) {
            return 'Condition must have a value key.';
        }

        return null;
    }
}
