<?php

namespace MintyPHP\Service\User;

use MintyPHP\Repository\Support\RepoQuery;
use MintyPHP\Repository\User\UserSavedFilterRepositoryInterface;

class UserSavedFilterService
{
    public const CONTEXT_ADDRESS_BOOK = 'address_book';
    private const NAME_MAX_LENGTH = 120;
    private const SEARCH_MAX_LENGTH = 200;
    private const MAX_PER_USER_CONTEXT = 20;

    public function __construct(private readonly UserSavedFilterRepositoryInterface $userSavedFilterRepository)
    {
    }

    public function listForAddressBook(int $userId): array
    {
        return $this->listByContext($userId, self::CONTEXT_ADDRESS_BOOK);
    }

    public function saveAddressBookFilter(int $userId, string $name, array $rawQuery): array
    {
        return $this->saveByContext($userId, self::CONTEXT_ADDRESS_BOOK, $name, $rawQuery);
    }

    public function deleteAddressBookFilter(int $userId, string $uuid): bool
    {
        return $this->deleteByContext($userId, self::CONTEXT_ADDRESS_BOOK, $uuid);
    }

    public function listByContext(int $userId, string $context): array
    {
        if ($userId <= 0 || $context === '') {
            return [];
        }
        $rows = $this->userSavedFilterRepository->listByUserAndContext($userId, $context);
        $list = [];
        foreach ($rows as $row) {
            $name = trim((string) ($row['name'] ?? ''));
            $uuid = trim((string) ($row['uuid'] ?? ''));
            if ($name === '' || $uuid === '') {
                continue;
            }
            $query = $this->normalizeQuery($this->decodeQuery((string) ($row['query_json'] ?? '')));
            $list[] = [
                'id' => (int) ($row['id'] ?? 0),
                'uuid' => $uuid,
                'name' => $name,
                'query' => $query,
                'created' => (string) ($row['created'] ?? ''),
            ];
        }
        return $list;
    }

    public function saveByContext(int $userId, string $context, string $name, array $rawQuery): array
    {
        $name = $this->truncate(trim($name), self::NAME_MAX_LENGTH);
        if ($userId <= 0 || $context === '') {
            return ['ok' => false, 'error' => 'invalid_user_or_context'];
        }
        if ($name === '') {
            return ['ok' => false, 'error' => 'name_required'];
        }
        $count = $this->userSavedFilterRepository->countByUserAndContext($userId, $context);
        if ($count >= self::MAX_PER_USER_CONTEXT) {
            return ['ok' => false, 'error' => 'max_reached'];
        }
        $query = $this->normalizeQuery($rawQuery);
        $queryJson = json_encode($query, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if (!is_string($queryJson)) {
            $queryJson = '{}';
        }
        $id = $this->userSavedFilterRepository->create([
            'user_id' => $userId,
            'context' => $context,
            'name' => $name,
            'query_json' => $queryJson,
        ]);
        if (!$id) {
            return ['ok' => false, 'error' => 'create_failed'];
        }
        return ['ok' => true, 'id' => (int) $id, 'name' => $name, 'query' => $query];
    }

    public function deleteByContext(int $userId, string $context, string $uuid): bool
    {
        if ($userId <= 0 || $context === '' || trim($uuid) === '') {
            return false;
        }
        return $this->userSavedFilterRepository->deleteByUuidForUserAndContext($uuid, $userId, $context);
    }

    private function decodeQuery(string $json): array
    {
        $decoded = json_decode($json, true);
        return is_array($decoded) ? $decoded : [];
    }

    private function normalizeQuery(array $rawQuery): array
    {
        $search = $this->truncate(trim((string) ($rawQuery['search'] ?? '')), self::SEARCH_MAX_LENGTH);
        $tenants = $this->normalizeUuidList($rawQuery['tenants'] ?? []);
        $departments = RepoQuery::normalizeIdList($rawQuery['departments'] ?? []);
        $roles = RepoQuery::normalizeIdList($rawQuery['roles'] ?? []);
        $custom = $this->normalizeCustomFieldQuery($rawQuery);

        $query = [];
        if ($search !== '') {
            $query['search'] = $search;
        }
        if ($tenants) {
            $query['tenants'] = $tenants;
        }
        if ($departments) {
            $query['departments'] = $departments;
        }
        if ($roles) {
            $query['roles'] = $roles;
        }
        foreach ($custom as $key => $value) {
            $query[$key] = $value;
        }

        return $query;
    }

    private function normalizeCustomFieldQuery(array $rawQuery): array
    {
        $normalized = [];
        foreach ($rawQuery as $rawKey => $rawValue) {
            $key = strtolower(trim((string) $rawKey));
            if ($key === '') {
                continue;
            }
            if (preg_match('/^cf_[a-f0-9-]{36}$/', $key)) {
                $value = trim((string) $rawValue);
                if ($value !== '' && preg_match('/^[a-z0-9_-]{1,64}$/i', $value)) {
                    $normalized[$key] = $value;
                }
                continue;
            }
            if (preg_match('/^cfm_[a-f0-9-]{36}$/', $key)) {
                $ids = RepoQuery::normalizeIdList($rawValue);
                if ($ids) {
                    $normalized[$key] = $ids;
                }
                continue;
            }
            if (preg_match('/^cfd_[a-f0-9-]{36}_(from|to)$/', $key)) {
                $value = trim((string) $rawValue);
                $dt = \DateTimeImmutable::createFromFormat('Y-m-d', $value);
                if ($dt && $dt->format('Y-m-d') === $value) {
                    $normalized[$key] = $value;
                }
            }
        }
        ksort($normalized, SORT_STRING);
        return $normalized;
    }

    private function normalizeUuidList($value): array
    {
        $items = $this->normalizeStringList($value);
        $list = [];
        foreach ($items as $item) {
            $item = strtolower($item);
            if (preg_match('/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/', $item)) {
                $list[] = $item;
            }
        }
        $list = array_values(array_unique($list));
        sort($list, SORT_STRING);
        return $list;
    }

    private function normalizeStringList($value): array
    {
        $raw = is_array($value) ? $value : [$value];
        $flat = [];
        $collect = static function ($item) use (&$collect, &$flat): void {
            if (is_array($item)) {
                foreach ($item as $nested) {
                    $collect($nested);
                }
                return;
            }
            $text = trim((string) $item);
            if ($text === '') {
                return;
            }
            if (str_contains($text, ',')) {
                foreach (explode(',', $text) as $part) {
                    $part = trim($part);
                    if ($part !== '') {
                        $flat[] = $part;
                    }
                }
                return;
            }
            $flat[] = $text;
        };
        foreach ($raw as $item) {
            $collect($item);
        }
        return array_values(array_unique($flat));
    }

    private function truncate(string $value, int $maxLength): string
    {
        if ($maxLength <= 0 || $value === '') {
            return $value;
        }
        if (function_exists('mb_strlen') && function_exists('mb_substr')) {
            if (mb_strlen($value) > $maxLength) {
                return mb_substr($value, 0, $maxLength);
            }
            return $value;
        }
        if (strlen($value) > $maxLength) {
            return substr($value, 0, $maxLength);
        }
        return $value;
    }
}
