<?php

declare(strict_types=1);

namespace WPStack\Sandbox\Domain;

final class ProductDefinition
{
    /**
     * @param array<string, mixed> $input
     * @return array{
     *   slug:string,name:string,description:string,file:string,landing:string,
     *   accent:string,initial_minutes:int,extension_minutes:int,
     *   maximum_minutes:int,extensions_allowed:bool,enabled:bool,
     *   dependencies:list<string>
     * }
     */
    public static function fromArray(array $input): array
    {
        $name = trim((string) ($input['name'] ?? ''));
        $file = trim((string) ($input['file'] ?? ''));
        if ($name === '' || !self::isPluginFile($file)) {
            throw new \InvalidArgumentException('A valid product name and plugin file are required.');
        }

        $slug = strtolower(trim((string) ($input['slug'] ?? $name)));
        $slug = preg_replace('/[^a-z0-9]+/', '-', $slug) ?? '';
        $slug = trim($slug, '-');
        if ($slug === '') {
            throw new \InvalidArgumentException('A valid product slug is required.');
        }

        $landing = trim((string) ($input['landing'] ?? '/wp-admin/'));
        if (!str_starts_with($landing, '/')) {
            $landing = '/wp-admin/' . ltrim($landing, '/');
        }
        if (!str_starts_with($landing, '/wp-admin/')) {
            $landing = '/wp-admin/';
        }
        if (str_contains($landing, '..') || preg_match('/[\r\n]/', $landing)) {
            $landing = '/wp-admin/';
        }

        $accent = strtolower(trim((string) ($input['accent'] ?? '#2563eb')));
        if (!preg_match('/^#[0-9a-f]{6}$/', $accent)) {
            $accent = '#2563eb';
        }

        $initial = self::clamp((int) ($input['initial_minutes'] ?? 6), 1, 1440);
        $allowed = self::boolean($input['extensions_allowed'] ?? true);
        $extension = $allowed
            ? self::clamp((int) ($input['extension_minutes'] ?? 6), 1, 1440)
            : 0;
        $maximum = $allowed
            ? self::clamp((int) ($input['maximum_minutes'] ?? 50), $initial, 1440)
            : $initial;

        $dependencies = [];
        foreach ((array) ($input['dependencies'] ?? []) as $dependency) {
            $dependency = trim((string) $dependency);
            if (self::isPluginFile($dependency) && $dependency !== $file) {
                $dependencies[] = $dependency;
            }
        }

        return [
            'slug' => $slug,
            'name' => $name,
            'description' => trim((string) ($input['description'] ?? '')),
            'file' => $file,
            'landing' => $landing,
            'accent' => $accent,
            'initial_minutes' => $initial,
            'extension_minutes' => $extension,
            'maximum_minutes' => $maximum,
            'extensions_allowed' => $allowed,
            'enabled' => self::boolean($input['enabled'] ?? true),
            'dependencies' => array_values(array_unique($dependencies)),
        ];
    }

    private static function isPluginFile(string $file): bool
    {
        return !str_contains($file, '..') && (bool) preg_match(
            '/^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+\.php$/',
            $file
        );
    }

    private static function clamp(int $value, int $minimum, int $maximum): int
    {
        return max($minimum, min($maximum, $value));
    }

    private static function boolean(mixed $value): bool
    {
        return filter_var($value, FILTER_VALIDATE_BOOL);
    }
}
