<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Core\Services;

use App\Framework\Mcp\Core\Interfaces\SchemaValidator;
use App\Framework\Mcp\Core\ValueObjects\ValidationResult;

/**
 * JSON Schema Validator für MCP Tools
 *
 * Implementiert einheitliche Validation für alle MCP Tool Input-Parameter
 */
final readonly class McpSchemaValidator implements SchemaValidator
{
    public function validate(array $data, array $schema): ValidationResult
    {
        try {
            $errors = [];
            $warnings = [];
            $validatedData = [];

            // Validate required fields
            if (isset($schema['required'])) {
                foreach ($schema['required'] as $field) {
                    if (! array_key_exists($field, $data)) {
                        $errors[] = "Required field '{$field}' is missing";
                    }
                }
            }

            // Validate properties
            if (isset($schema['properties'])) {
                foreach ($schema['properties'] as $property => $propertySchema) {
                    if (array_key_exists($property, $data)) {
                        $validationResult = $this->validateProperty(
                            $property,
                            $data[$property],
                            $propertySchema
                        );

                        if (! $validationResult['valid']) {
                            $errors = array_merge($errors, $validationResult['errors']);
                        } else {
                            $validatedData[$property] = $validationResult['value'];
                        }

                        if (! empty($validationResult['warnings'])) {
                            $warnings = array_merge($warnings, $validationResult['warnings']);
                        }
                    } elseif (isset($propertySchema['default'])) {
                        // Set default value
                        $validatedData[$property] = $propertySchema['default'];
                    }
                }
            }

            // Check for unknown properties if additionalProperties is false
            if (isset($schema['additionalProperties']) && $schema['additionalProperties'] === false) {
                $allowedProperties = array_keys($schema['properties'] ?? []);
                foreach (array_keys($data) as $property) {
                    if (! in_array($property, $allowedProperties, true)) {
                        $warnings[] = "Unknown property '{$property}' will be ignored";
                    }
                }
            }

            if (! empty($errors)) {
                return ValidationResult::failure($errors, $warnings);
            }

            return empty($warnings)
                ? ValidationResult::success($validatedData)
                : ValidationResult::withWarnings($validatedData, $warnings);

        } catch (\Throwable $e) {
            return ValidationResult::failure([
                "Validation failed: {$e->getMessage()}",
            ]);
        }
    }

    public function validateFields(array $data, array $schema, array $fields): ValidationResult
    {
        // Create a filtered schema with only the specified fields
        $filteredSchema = [
            'type' => 'object',
            'properties' => [],
            'additionalProperties' => $schema['additionalProperties'] ?? true,
        ];

        if (isset($schema['properties'])) {
            foreach ($fields as $field) {
                if (isset($schema['properties'][$field])) {
                    $filteredSchema['properties'][$field] = $schema['properties'][$field];
                }
            }
        }

        // Only require fields that are both in the original required list and in our field list
        if (isset($schema['required'])) {
            $filteredSchema['required'] = array_intersect($schema['required'], $fields);
        }

        return $this->validate($data, $filteredSchema);
    }

    public function isValidSchema(array $schema): bool
    {
        try {
            // Basic schema structure validation
            if (! isset($schema['type'])) {
                return false;
            }

            // Check if type is valid
            $validTypes = ['object', 'array', 'string', 'number', 'integer', 'boolean', 'null'];
            if (! in_array($schema['type'], $validTypes, true)) {
                return false;
            }

            // For object type, validate properties structure
            if ($schema['type'] === 'object' && isset($schema['properties'])) {
                if (! is_array($schema['properties'])) {
                    return false;
                }

                // Recursively validate property schemas
                foreach ($schema['properties'] as $property => $propertySchema) {
                    if (! is_array($propertySchema) || ! $this->isValidPropertySchema($propertySchema)) {
                        return false;
                    }
                }
            }

            return true;

        } catch (\Throwable) {
            return false;
        }
    }

    private function validateProperty(string $name, mixed $value, array $schema): array
    {
        $result = [
            'valid' => true,
            'value' => $value,
            'errors' => [],
            'warnings' => [],
        ];

        // Type validation
        if (isset($schema['type'])) {
            $typeValid = $this->validateType($value, $schema['type']);
            if (! $typeValid) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must be of type '{$schema['type']}'";

                return $result;
            }
        }

        // Enum validation
        if (isset($schema['enum']) && ! in_array($value, $schema['enum'], true)) {
            $result['valid'] = false;
            $allowedValues = implode(', ', $schema['enum']);
            $result['errors'][] = "Property '{$name}' must be one of: {$allowedValues}";

            return $result;
        }

        // String specific validations
        if (is_string($value)) {
            if (isset($schema['minLength']) && strlen($value) < $schema['minLength']) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must be at least {$schema['minLength']} characters long";
            }

            if (isset($schema['maxLength']) && strlen($value) > $schema['maxLength']) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must be at most {$schema['maxLength']} characters long";
            }

            if (isset($schema['pattern']) && ! preg_match('/' . $schema['pattern'] . '/', $value)) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' does not match the required pattern";
            }
        }

        // Number specific validations
        if (is_numeric($value)) {
            if (isset($schema['minimum']) && $value < $schema['minimum']) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must be at least {$schema['minimum']}";
            }

            if (isset($schema['maximum']) && $value > $schema['maximum']) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must be at most {$schema['maximum']}";
            }
        }

        // Array specific validations
        if (is_array($value)) {
            if (isset($schema['minItems']) && count($value) < $schema['minItems']) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must have at least {$schema['minItems']} items";
            }

            if (isset($schema['maxItems']) && count($value) > $schema['maxItems']) {
                $result['valid'] = false;
                $result['errors'][] = "Property '{$name}' must have at most {$schema['maxItems']} items";
            }
        }

        return $result;
    }

    private function validateType(mixed $value, string $expectedType): bool
    {
        return match ($expectedType) {
            'string' => is_string($value),
            'integer' => is_int($value),
            'number' => is_numeric($value),
            'boolean' => is_bool($value),
            'array' => is_array($value),
            'object' => is_array($value) || is_object($value),
            'null' => $value === null,
            default => false
        };
    }

    private function isValidPropertySchema(array $schema): bool
    {
        // Basic property schema validation
        if (isset($schema['type'])) {
            $validTypes = ['string', 'number', 'integer', 'boolean', 'array', 'object', 'null'];
            if (! in_array($schema['type'], $validTypes, true)) {
                return false;
            }
        }

        // Enum values must be array
        if (isset($schema['enum']) && ! is_array($schema['enum'])) {
            return false;
        }

        return true;
    }
}
