<?php

declare(strict_types=1);

namespace App\Framework\Http\Session\ValueObjects;

/**
 * Value Object representing a collection of validation errors grouped by form
 */
final readonly class ValidationErrorCollection
{
    /**
     * @param array<string, array<string, ValidationError[]>> $errors Errors indexed by formId -> field -> errors[]
     */
    public function __construct(public array $errors = [])
    {
    }

    /**
     * Create from raw array (compatible with existing ValidationErrorBag format)
     * @param array<string, array<string, string[]>> $rawData
     */
    public static function fromArray(array $rawData): self
    {
        $errors = [];

        foreach ($rawData as $formId => $formErrors) {
            if (! is_array($formErrors)) {
                continue;
            }

            $errors[$formId] = [];
            foreach ($formErrors as $field => $fieldErrors) {
                if (! is_array($fieldErrors)) {
                    $fieldErrors = [$fieldErrors];
                }

                $errors[$formId][$field] = [];
                foreach ($fieldErrors as $errorMessage) {
                    if (is_string($errorMessage)) {
                        $errors[$formId][$field][] = ValidationError::create($field, $errorMessage);
                    }
                }
            }
        }

        return new self($errors);
    }

    /**
     * Convert to array (compatible with existing ValidationErrorBag format)
     * @return array<string, array<string, string[]>>
     */
    public function toArray(): array
    {
        $result = [];

        foreach ($this->errors as $formId => $formErrors) {
            $result[$formId] = [];
            foreach ($formErrors as $field => $validationErrors) {
                $result[$formId][$field] = [];
                foreach ($validationErrors as $validationError) {
                    $result[$formId][$field][] = $validationError->message;
                }
            }
        }

        return $result;
    }

    /**
     * Get all errors for a form
     * @return array<string, ValidationError[]>
     */
    public function getFormErrors(string $formId): array
    {
        return $this->errors[$formId] ?? [];
    }

    /**
     * Get errors for a specific field
     * @return ValidationError[]
     */
    public function getFieldErrors(string $formId, string $field): array
    {
        return $this->errors[$formId][$field] ?? [];
    }

    /**
     * Get error messages for a specific field
     * @return string[]
     */
    public function getFieldErrorMessages(string $formId, string $field): array
    {
        $errors = $this->getFieldErrors($formId, $field);

        return array_map(fn ($error) => $error->message, $errors);
    }

    /**
     * Get first error message for a field
     */
    public function getFirstFieldError(string $formId, string $field): ?string
    {
        $errors = $this->getFieldErrors($formId, $field);

        return $errors[0]->message ?? null;
    }

    /**
     * Check if form has errors
     */
    public function hasFormErrors(string $formId): bool
    {
        return ! empty($this->errors[$formId]);
    }

    /**
     * Check if field has errors
     */
    public function hasFieldErrors(string $formId, string $field): bool
    {
        return ! empty($this->errors[$formId][$field]);
    }

    /**
     * Add error to form and field
     */
    public function withError(string $formId, ValidationError $error): self
    {
        $errors = $this->errors;

        if (! isset($errors[$formId])) {
            $errors[$formId] = [];
        }

        if (! isset($errors[$formId][$error->field])) {
            $errors[$formId][$error->field] = [];
        }

        $errors[$formId][$error->field][] = $error;

        return new self($errors);
    }

    /**
     * Add multiple errors for a form
     * @param ValidationError[] $validationErrors
     */
    public function withFormErrors(string $formId, array $validationErrors): self
    {
        $result = $this;

        foreach ($validationErrors as $error) {
            $result = $result->withError($formId, $error);
        }

        return $result;
    }

    /**
     * Remove all errors for a form
     */
    public function withoutForm(string $formId): self
    {
        $errors = $this->errors;
        unset($errors[$formId]);

        return new self($errors);
    }

    /**
     * Remove errors for a specific field
     */
    public function withoutField(string $formId, string $field): self
    {
        $errors = $this->errors;

        if (isset($errors[$formId][$field])) {
            unset($errors[$formId][$field]);

            // Remove form if no fields left
            if (empty($errors[$formId])) {
                unset($errors[$formId]);
            }
        }

        return new self($errors);
    }

    /**
     * Get all form IDs with errors
     * @return string[]
     */
    public function getFormIds(): array
    {
        return array_keys($this->errors);
    }

    /**
     * Get all field names for a form
     * @return string[]
     */
    public function getFieldNames(string $formId): array
    {
        return array_keys($this->errors[$formId] ?? []);
    }

    /**
     * Check if collection is empty
     */
    public function isEmpty(): bool
    {
        return empty($this->errors);
    }

    /**
     * Get total error count
     */
    public function count(): int
    {
        $count = 0;
        foreach ($this->errors as $formErrors) {
            foreach ($formErrors as $fieldErrors) {
                $count += count($fieldErrors);
            }
        }

        return $count;
    }

    /**
     * Get error count for a form
     */
    public function getFormErrorCount(string $formId): int
    {
        if (! isset($this->errors[$formId])) {
            return 0;
        }

        $count = 0;
        foreach ($this->errors[$formId] as $fieldErrors) {
            $count += count($fieldErrors);
        }

        return $count;
    }

    /**
     * Get all errors as flat array
     * @return ValidationError[]
     */
    public function getAllErrors(): array
    {
        $result = [];
        foreach ($this->errors as $formErrors) {
            foreach ($formErrors as $fieldErrors) {
                $result = array_merge($result, $fieldErrors);
            }
        }

        return $result;
    }
}
