<?php

namespace MintyPHP\Module\Helpdesk\Service;

use MintyPHP\Module\Helpdesk\Repository\SoftwareProductRepository;

/** @api Called from pages/helpdesk/software-products action files */
class SoftwareProductService
{
    public function __construct(
        private readonly SoftwareProductRepository $repository
    ) {
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findByCode(string $code): ?array
    {
        return $this->repository->findByCode($code);
    }

    /**
     * @return array{total: int, rows: list<array<string, mixed>>}
     */
    public function listPaged(array $filters): array
    {
        return $this->repository->listPaged($filters);
    }

    /**
     * List active products that have a handover protocol schema defined.
     *
     * @return list<array<string, mixed>>
     */
    public function listWithSchema(): array
    {
        return $this->repository->listWithSchema();
    }

    private const ALLOWED_FIELD_TYPES = ['heading', 'paragraph', 'text', 'textarea', 'number', 'date', 'checkbox', 'select'];
    private const DISPLAY_ONLY_TYPES = ['heading', 'paragraph'];
    private const MAX_SCHEMA_FIELDS = 50;

    /**
     * Validate and save a handover protocol schema for a software product.
     *
     * @param list<array<string, mixed>> $fields
     * @return array{ok: bool, errors: array<string, string>}
     */
    public function saveHandoverSchema(string $code, array $fields): array
    {
        $errors = [];

        $product = $this->repository->findByCode($code);
        if ($product === null) {
            return ['ok' => false, 'errors' => ['schema' => t('Software product not found')]];
        }

        if ($fields === []) {
            $this->repository->updateHandoverSchema($code, null);

            return ['ok' => true, 'errors' => []];
        }

        if (count($fields) > self::MAX_SCHEMA_FIELDS) {
            $errors['schema'] = t('Maximum %d fields allowed', self::MAX_SCHEMA_FIELDS);

            return ['ok' => false, 'errors' => $errors];
        }

        $seenKeys = [];

        foreach ($fields as $index => $field) {
            $fieldNum = $index + 1;
            $type = (string) ($field['type'] ?? '');
            $label = trim((string) ($field['label'] ?? ''));
            $isDisplayOnly = in_array($type, self::DISPLAY_ONLY_TYPES, true);

            if (!in_array($type, self::ALLOWED_FIELD_TYPES, true)) {
                $errors["field_{$fieldNum}_type"] = t('Field %d: unknown type "%s"', $fieldNum, $type);
                continue;
            }

            if ($label === '') {
                $errors["field_{$fieldNum}_label"] = t('Field %d: label is required', $fieldNum);
            }

            if (!$isDisplayOnly && $label !== '') {
                $key = self::slugify($label);
                if (isset($seenKeys[$key])) {
                    $errors["field_{$fieldNum}_key"] = t('Field %d: generates duplicate key "%s" — use a more distinct label', $fieldNum, $key);
                } else {
                    $seenKeys[$key] = true;
                }
            }

            if ($type === 'select') {
                $options = $field['options'] ?? [];
                if (!is_array($options) || $options === []) {
                    $errors["field_{$fieldNum}_options"] = t('Field %d: select must have at least one option', $fieldNum);
                } else {
                    foreach ($options as $optIdx => $opt) {
                        $optNum = $optIdx + 1;
                        if (trim((string) ($opt['label'] ?? '')) === '') {
                            $errors["field_{$fieldNum}_option_{$optNum}"] = t('Field %d, option %d: label is required', $fieldNum, $optNum);
                        }
                    }
                }
            }
        }

        if ($errors !== []) {
            return ['ok' => false, 'errors' => $errors];
        }

        $normalized = $this->normalizeSchemaFields($fields);

        $currentSchema = $product['handover_protocol_schema'] ?? null;
        $currentVersion = 0;
        if ($currentSchema !== null) {
            $decoded = json_decode($currentSchema, true);
            $currentVersion = (int) ($decoded['version'] ?? 0);
        }

        $schema = [
            'version' => $currentVersion + 1,
            'fields' => $normalized,
        ];

        $json = json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        $this->repository->updateHandoverSchema($code, $json);

        return ['ok' => true, 'errors' => []];
    }

    /**
     * @param list<array<string, mixed>> $fields
     * @return list<array<string, mixed>>
     */
    private function normalizeSchemaFields(array $fields): array
    {
        $normalized = [];

        foreach ($fields as $field) {
            $type = (string) ($field['type'] ?? '');
            $isDisplayOnly = in_array($type, self::DISPLAY_ONLY_TYPES, true);

            $entry = ['type' => $type, 'label' => trim((string) ($field['label'] ?? ''))];

            if (!$isDisplayOnly) {
                $entry['key'] = self::slugify($entry['label']);
                $entry['required'] = !empty($field['required']);
            }

            if ($type === 'select' && is_array($field['options'] ?? null)) {
                $entry['options'] = [];
                foreach ($field['options'] as $opt) {
                    $label = trim((string) ($opt['label'] ?? ''));
                    $entry['options'][] = [
                        'value' => self::slugify($label),
                        'label' => $label,
                    ];
                }
            }

            $normalized[] = $entry;
        }

        return $normalized;
    }

    private static function slugify(string $text): string
    {
        $slug = mb_strtolower($text);
        $slug = preg_replace('/[äÄ]/', 'ae', $slug);
        $slug = preg_replace('/[öÖ]/', 'oe', $slug);
        $slug = preg_replace('/[üÜ]/', 'ue', $slug);
        $slug = preg_replace('/ß/', 'ss', $slug);
        $slug = preg_replace('/[^a-z0-9]+/', '_', $slug);
        $slug = trim($slug, '_');

        return $slug === '' ? 'option' : $slug;
    }

    /**
     * Update the user-managed name field for a software product.
     *
     * @return array{ok: bool, form: array<string, mixed>, errors: array<string, string>}
     */
    public function updateNameByCode(string $code, string $name): array
    {
        $name = trim($name);
        $errors = [];

        if (mb_strlen($name) > 255) {
            $errors['name'] = t('Name must not exceed 255 characters');
        }

        $product = $this->repository->findByCode($code);
        if ($product === null) {
            $errors['code'] = t('Software product not found');
        }

        $form = array_merge($product ?? [], ['name' => $name]);

        if ($errors !== []) {
            return ['ok' => false, 'form' => $form, 'errors' => $errors];
        }

        $updated = $this->repository->updateName($code, $name);
        if (!$updated) {
            return ['ok' => false, 'form' => $form, 'errors' => ['name' => t('Failed to save')]];
        }

        $form = $this->repository->findByCode($code) ?? $form;

        return ['ok' => true, 'form' => $form, 'errors' => []];
    }
}
