<?php
/**
 * Validation Exception
 *
 * @package Licenzo\Framework\Exceptions
 */

namespace Licenzo\Framework\Exceptions;

use Exception;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Validation Exception for handling validation errors
 */
class ValidationException extends Exception
{
    /**
     * Validation errors
     *
     * @var array
     */
    protected $errors = [];

    /**
     * Field that failed validation
     *
     * @var string|null
     */
    protected $field;

    /**
     * Constructor
     *
     * @param string $message Error message
     * @param array $errors Validation errors
     * @param string|null $field Field that failed validation
     * @param Exception|null $previous Previous exception
     */
    public function __construct(
        string $message = 'Validation failed',
        array $errors = [],
        string $field = null,
        Exception $previous = null
    ) {
        $this->errors = $errors;
        $this->field = $field;

        parent::__construct($message, 422, $previous);
    }

    /**
     * Get validation errors
     *
     * @return array
     */
    public function getErrors(): array
    {
        return $this->errors;
    }

    /**
     * Get field that failed validation
     *
     * @return string|null
     */
    public function getField(): ?string
    {
        return $this->field;
    }

    /**
     * Check if there are validation errors
     *
     * @return bool
     */
    public function hasErrors(): bool
    {
        return !empty($this->errors);
    }

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

    /**
     * Check if a specific field has errors
     *
     * @param string $field
     * @return bool
     */
    public function hasFieldErrors(string $field): bool
    {
        return isset($this->errors[$field]) && !empty($this->errors[$field]);
    }

    /**
     * Get first error message
     *
     * @return string|null
     */
    public function getFirstError(): ?string
    {
        if (empty($this->errors)) {
            return null;
        }

        $firstField = array_key_first($this->errors);
        $fieldErrors = $this->errors[$firstField];

        return is_array($fieldErrors) ? ($fieldErrors[0] ?? null) : $fieldErrors;
    }

    /**
     * Get all error messages as flat array
     *
     * @return array
     */
    public function getAllMessages(): array
    {
        $messages = [];

        foreach ($this->errors as $field => $fieldErrors) {
            if (is_array($fieldErrors)) {
                $messages = array_merge($messages, $fieldErrors);
            } else {
                $messages[] = $fieldErrors;
            }
        }

        return $messages;
    }

    /**
     * Set validation errors
     *
     * @param array $errors
     * @return $this
     */
    public function setErrors(array $errors): self
    {
        $this->errors = $errors;
        return $this;
    }

    /**
     * Add validation error for a field
     *
     * @param string $field
     * @param string $message
     * @return $this
     */
    public function addError(string $field, string $message): self
    {
        if (!isset($this->errors[$field])) {
            $this->errors[$field] = [];
        }

        if (!is_array($this->errors[$field])) {
            $this->errors[$field] = [$this->errors[$field]];
        }

        $this->errors[$field][] = $message;
        return $this;
    }

    /**
     * Add multiple errors for a field
     *
     * @param string $field
     * @param array $messages
     * @return $this
     */
    public function addFieldErrors(string $field, array $messages): self
    {
        foreach ($messages as $message) {
            $this->addError($field, $message);
        }
        return $this;
    }

    /**
     * Set field that failed validation
     *
     * @param string $field
     * @return $this
     */
    public function setField(string $field): self
    {
        $this->field = $field;
        return $this;
    }

    /**
     * Create validation exception with single field error
     *
     * @param string $field
     * @param string $message
     * @param string|null $generalMessage
     * @return static
     */
    public static function withFieldError(
        string $field,
        string $message,
        string $generalMessage = null
    ): self {
        return new static(
            $generalMessage ?? "Validation failed for field: {$field}",
            [$field => [$message]],
            $field
        );
    }

    /**
     * Create validation exception with multiple field errors
     *
     * @param array $errors
     * @param string|null $message
     * @return static
     */
    public static function withErrors(array $errors, string $message = null): self
    {
        return new static(
            $message ?? 'Validation failed',
            $errors
        );
    }

    /**
     * Create validation exception for required field
     *
     * @param string $field
     * @return static
     */
    public static function required(string $field): self
    {
        return static::withFieldError(
            $field,
            "The {$field} field is required.",
            "Required field missing: {$field}"
        );
    }

    /**
     * Create validation exception for invalid format
     *
     * @param string $field
     * @param string $format
     * @return static
     */
    public static function invalidFormat(string $field, string $format): self
    {
        return static::withFieldError(
            $field,
            "The {$field} field must be a valid {$format}.",
            "Invalid format for field: {$field}"
        );
    }

    /**
     * Create validation exception for invalid length
     *
     * @param string $field
     * @param int $min
     * @param int|null $max
     * @return static
     */
    public static function invalidLength(string $field, int $min, int $max = null): self
    {
        if ($max) {
            $message = "The {$field} field must be between {$min} and {$max} characters.";
        } else {
            $message = "The {$field} field must be at least {$min} characters.";
        }

        return static::withFieldError($field, $message);
    }

    /**
     * Create validation exception for invalid value
     *
     * @param string $field
     * @param array $allowedValues
     * @return static
     */
    public static function invalidValue(string $field, array $allowedValues): self
    {
        $allowed = implode(', ', $allowedValues);
        return static::withFieldError(
            $field,
            "The {$field} field must be one of: {$allowed}."
        );
    }

    /**
     * Convert to array for API response
     *
     * @return array
     */
    public function toArray(): array
    {
        return [
            'message' => $this->getMessage(),
            'errors' => $this->getErrors(),
            'field' => $this->getField(),
        ];
    }
}
