<?php

namespace App\Modules\HR\Analytics\Services;

use App\Modules\HR\Analytics\Repositories\HrAnalyticsRepository;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class HrAnalyticsService
{
    public function __construct(private readonly HrAnalyticsRepository $analytics) {}

    /** @return array<string, mixed> */
    public function dashboard(int $tenantId): array
    {
        $months = $this->months();
        $from = $months->first()->copy()->startOfMonth()->toDateString();
        $employees = $this->analytics->employees($tenantId);
        $applications = $this->analytics->applications($tenantId, $from);

        return [
            'cards' => $this->cards($employees, $applications),
            'headcount' => $this->headcount($employees, $months),
            'turnover' => $this->turnover($employees, $months),
            'payroll' => $this->trend($this->analytics->payroll($tenantId, $from), $months),
            'overtime' => $this->trend($this->analytics->overtime($tenantId, $from), $months),
            'leave' => $this->trend($this->analytics->leave($tenantId, $from), $months),
            'absenteeism' => $this->trend($this->analytics->absences($tenantId, $from), $months, false),
            'departments' => $this->distribution($employees, 'department'),
            'genders' => $this->distribution($employees, 'gender'),
            'funnel' => $applications->countBy('status')->sortDesc(),
        ];
    }

    private function months(): Collection
    {
        return collect(range(11, 0))->map(
            fn (int $offset) => now()->subMonths($offset)->startOfMonth()
        );
    }

    /** @return array<string, int|float> */
    private function cards(Collection $employees, Collection $applications): array
    {
        $hired = $applications->whereNotNull('hired_at');
        $days = $hired->map(fn ($row) => Carbon::parse($row->applied_at)->diffInDays(Carbon::parse($row->hired_at)));

        return [
            'active_headcount' => $employees->where('is_active', true)->count(),
            'inactive_employees' => $employees->where('is_active', false)->count(),
            'applications' => $applications->count(),
            'average_time_to_hire' => round((float) ($days->avg() ?? 0), 1),
        ];
    }

    private function headcount(Collection $employees, Collection $months): Collection
    {
        return $months->mapWithKeys(function (Carbon $month) use ($employees): array {
            $end = $month->copy()->endOfMonth();
            $count = $employees->filter(function ($employee) use ($end): bool {
                $hired = Carbon::parse($employee->hire_date)->lte($end);
                $inactiveAfter = (bool) $employee->is_active || Carbon::parse($employee->updated_at)->gt($end);

                return $hired && $inactiveAfter;
            })->count();

            return [$month->format('Y-m') => $count];
        });
    }

    private function turnover(Collection $employees, Collection $months): Collection
    {
        return $months->mapWithKeys(function (Carbon $month) use ($employees): array {
            $count = $employees->where('is_active', false)->filter(
                fn ($employee) => Carbon::parse($employee->updated_at)->format('Y-m') === $month->format('Y-m')
            )->count();

            return [$month->format('Y-m') => $count];
        });
    }

    private function trend(Collection $rows, Collection $months, bool $sum = true): Collection
    {
        return $months->mapWithKeys(function (Carbon $month) use ($rows, $sum): array {
            $matching = $rows->filter(
                fn ($row) => Carbon::parse($row->date)->format('Y-m') === $month->format('Y-m')
            );
            $value = $sum && isset($matching->first()->value)
                ? round((float) $matching->sum('value'), 2)
                : $matching->count();

            return [$month->format('Y-m') => $value];
        });
    }

    private function distribution(Collection $employees, string $field): Collection
    {
        return $employees->where('is_active', true)
            ->map(fn ($employee) => $employee->{$field} ?: __('common.unknown'))
            ->countBy()->sortDesc();
    }
}
