<?php

namespace App\Domains\Shared\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class CroppedImageOrBase64Rule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (! is_array($value)) {
            (new ImageOrBase64Rule)->validate($attribute, $value, $fail);

            return;
        }

        if (! array_key_exists('image', $value)) {
            $fail('The :attribute.image field is required.');

            return;
        }

        $valid = true;
        (new ImageOrBase64Rule)->validate(
            "{$attribute}.image",
            $value['image'],
            function (string $message) use ($fail, &$valid): void {
                $valid = false;
                $fail($message);
            }
        );

        if (! $valid) {
            return;
        }

        if (isset($value['crops']) && is_array($value['crops'])) {
            foreach (['desktop', 'mobile'] as $variant) {
                $crop = $value['crops'][$variant] ?? null;

                if (! is_array($crop)) {
                    $fail("The :attribute.crops.{$variant} field must be an object.");

                    continue;
                }

                $this->validateRange("{$attribute}.crops.{$variant}", 'crop_horizontal', $crop, $fail);
                $this->validateRange("{$attribute}.crops.{$variant}", 'crop_vertical', $crop, $fail);
            }

            return;
        }

        // Backwards compatibility with the original single-crop payload.
        $this->validateRange($attribute, 'crop_horizontal', $value, $fail);
        $this->validateRange($attribute, 'crop_vertical', $value, $fail);
    }

    private function validateRange(string $attribute, string $axis, array $value, Closure $fail): void
    {
        $range = $value[$axis] ?? null;

        if (! is_array($range)) {
            $fail("The :attribute.{$axis} field must be an object.");

            return;
        }

        $start = $range['start_percentage'] ?? null;
        $end = $range['end_percentage'] ?? null;

        if (
            ! is_numeric($start)
            || ! is_numeric($end)
            || (float) $start < 0
            || (float) $end > 100
            || (float) $start >= (float) $end
        ) {
            $fail(
                "The :attribute.{$axis} field must satisfy "
                .'0 <= start_percentage < end_percentage <= 100.'
            );
        }
    }
}
