<?php

namespace App\Services;

use App\Models\JourneyEventModel;
use CodeIgniter\Shield\Entities\User;
use DateTimeImmutable;

class JourneyService
{
    private AuthorizationService $authorizationService;

    public function __construct()
    {
        helper(['academy', 'url']);

        $this->authorizationService = service('authorization');
    }

    /**
     * @param array<string, mixed> $event
     */
    public function recordEvent(array $event): int
    {
        $model = model(JourneyEventModel::class);

        $payload = array_merge([
            'metadata' => null,
            'created_at' => date('Y-m-d H:i:s'),
        ], $event);

        $model->insert($payload);

        return (int) $model->getInsertID();
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function getIndexData(array $input, User $user): array
    {
        $filters = $this->buildFilters($input, (int) $user->id);
        $events  = $this->getEvents($filters);

        return [
            'pageTitle' => 'Journey',
            'pageDescription' => 'Timeline operational pentru copii, cu evenimente generate automat din evaluari, profil si prezenta.',
            'filters' => $filters,
            'summary' => $this->buildSummary($events),
            'events' => $events,
        ];
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    private function buildFilters(array $input, int $userId): array
    {
        $role           = $this->authorizationService->getPrimaryRole($userId);
        $lockedTrainerId  = $role === 'coach' ? $this->authorizationService->getCoachIdForUser($userId) : null;
        $selectedPeriod = (string) ($input['period'] ?? '30');
        $today          = new DateTimeImmutable('today');
        [$dateStart, $dateEnd] = $this->resolveDates($selectedPeriod, $today);
        $query          = trim((string) ($input['q'] ?? ''));
        $selectedType   = trim((string) ($input['event_type'] ?? 'all'));
        $eventOptions   = $this->eventTypeOptions($lockedTrainerId);

        if ($selectedType !== 'all' && ! array_key_exists($selectedType, $eventOptions)) {
            $selectedType = 'all';
        }

        return [
            'current_role' => $role,
            'coach_locked' => $lockedTrainerId !== null,
            'selected_period' => $selectedPeriod,
            'selected_event_type' => $selectedType,
            'query' => $query,
            'date_start' => $dateStart,
            'date_end' => $dateEnd,
            'period_options' => [
                '7' => 'Ultimele 7 zile',
                '30' => 'Ultimele 30 zile',
                '90' => 'Ultimele 90 zile',
                'month' => 'Luna curenta',
            ],
            'event_type_options' => ['all' => 'Toate evenimentele'] + $eventOptions,
            'allowed_child_ids' => $this->allowedChildIds($lockedTrainerId),
        ];
    }

    /**
     * @return array{0: string, 1: string}
     */
    private function resolveDates(string $selectedPeriod, DateTimeImmutable $today): array
    {
        if ($selectedPeriod === 'month') {
            return [
                $today->modify('first day of this month')->format('Y-m-d'),
                $today->format('Y-m-d'),
            ];
        }

        $days = (int) $selectedPeriod;
        if (! in_array($days, [7, 30, 90], true)) {
            $days = 30;
        }

        return [
            $today->modify('-' . ($days - 1) . ' days')->format('Y-m-d'),
            $today->format('Y-m-d'),
        ];
    }

    /**
     * @param array<string, mixed> $filters
     * @return list<array<string, mixed>>
     */
    private function getEvents(array $filters): array
    {
        $builder = db_connect()
            ->table('journey_events events')
            ->select('events.*, children.full_name as child_name, users.username as actor_name')
            ->join('children', 'children.id = events.child_id')
            ->join('users', 'users.id = events.created_by', 'left')
            ->where('events.created_at >=', $filters['date_start'] . ' 00:00:00')
            ->where('events.created_at <=', $filters['date_end'] . ' 23:59:59')
            ->orderBy('events.created_at', 'DESC')
            ->orderBy('events.id', 'DESC');

        if ($filters['allowed_child_ids'] !== null) {
            if ($filters['allowed_child_ids'] === []) {
                return [];
            }

            $builder->whereIn('events.child_id', $filters['allowed_child_ids']);
        }

        if ($filters['selected_event_type'] !== 'all') {
            $builder->where('events.event_type', $filters['selected_event_type']);
        }

        if ($filters['query'] !== '') {
            $builder
                ->groupStart()
                ->like('children.full_name', $filters['query'])
                ->orLike('events.title', $filters['query'])
                ->orLike('events.description', $filters['query'])
                ->groupEnd();
        }

        return $builder->get()->getResultArray();
    }

    /**
     * @param list<array<string, mixed>> $events
     * @return list<array<string, mixed>>
     */
    private function buildSummary(array $events): array
    {
        $counts = [];
        foreach ($events as $event) {
            $type = (string) $event['event_type'];
            if (! isset($counts[$type])) {
                $counts[$type] = 0;
            }

            $counts[$type]++;
        }

        arsort($counts);
        $topType = $counts === [] ? ['label' => '-', 'value' => 0] : ['label' => status_label((string) array_key_first($counts)), 'value' => (int) current($counts)];

        return [
            ['label' => 'Evenimente in selectie', 'value' => count($events), 'meta' => 'Timeline filtrat dupa perioada'],
            ['label' => 'Tip dominant', 'value' => $topType['label'], 'meta' => 'Aparitii: ' . $topType['value']],
        ];
    }

    /**
     * @return array<string, string>
     */
    private function eventTypeOptions(?int $lockedTrainerId): array
    {
        $builder = db_connect()
            ->table('journey_events events')
            ->distinct()
            ->select('events.event_type')
            ->orderBy('events.event_type', 'ASC');

        $allowedChildIds = $this->allowedChildIds($lockedTrainerId);
        if ($allowedChildIds !== null) {
            if ($allowedChildIds === []) {
                return [];
            }

            $builder->whereIn('events.child_id', $allowedChildIds);
        }

        $rows    = $builder->get()->getResultArray();
        $options = [];
        foreach ($rows as $row) {
            $options[$row['event_type']] = status_label($row['event_type']);
        }

        return $options;
    }

    /**
     * @return list<int>|null
     */
    private function allowedChildIds(?int $lockedTrainerId): ?array
    {
        if ($lockedTrainerId === null) {
            return null;
        }

        return array_values(array_map(
            static fn (array $row): int => (int) $row['id'],
            db_connect()
                ->table('children')
                ->select('id')
                ->where('primary_coach_id', $lockedTrainerId)
                ->get()
                ->getResultArray(),
        ));
    }
}
