<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\HourRegistration\Services;

use Carbon\Carbon;
use Illuminate\Contracts\Auth\Authenticatable;
use Maestro\Core\Modules\HourRegistration\Enums\HourLineApprovalStatus;
use Maestro\Core\Modules\HourRegistration\Enums\HourWorkflowStatus;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetLine;
use Symfony\Component\HttpFoundation\StreamedResponse;

final class HourCsvExportService
{
    public function __construct(
        private readonly HourApproverResolver $approverResolver,
        private readonly HourTotalsCalculator $totalsCalculator,
    ) {}

    /**
     * @param  array{
     *   employee_ids?: list<int|string>,
     *   customer_id?: int|null,
     *   hour_project_id?: int|null,
     *   from?: string|null,
     *   to?: string|null,
     * }  $filters
     */
    public function streamCsv(Authenticatable $user, array $filters): StreamedResponse
    {
        abort_unless($user->can('hours.export') || $user->can('hours.admin'), 403);

        $filename = sprintf('hours-export-%s.csv', Carbon::now()->format('Y-m-d_H-i'));

        $query = HourTimesheetLine::query()
            ->with(['week.user', 'internalCategory', 'customer', 'hourProject.customer', 'hourTask'])
            ->where('line_approval_status', HourLineApprovalStatus::Approved)
            ->whereHas('week', function ($w) use ($filters): void {
                $w->where('workflow_status', HourWorkflowStatus::Approved);
                if (($filters['from'] ?? null) !== null && $filters['from'] !== '') {
                    $w->whereDate('week_start', '>=', $filters['from']);
                }

                if (($filters['to'] ?? null) !== null && $filters['to'] !== '') {
                    $w->whereDate('week_start', '<=', $filters['to']);
                }

                if (($filters['employee_ids'] ?? null) !== null && $filters['employee_ids'] !== []) {
                    $ids = array_values(array_unique(array_map('intval', $filters['employee_ids'])));
                    $w->whereIn('user_id', $ids);
                }
            });

        if (($filters['customer_id'] ?? null) !== null) {
            $cid = (int) $filters['customer_id'];
            $query->where(function ($q) use ($cid): void {
                $q->where('customer_id', $cid)
                    ->orWhereHas('hourProject', fn ($p) => $p->where('customer_id', $cid));
            });
        }

        if (($filters['hour_project_id'] ?? null) !== null) {
            $query->where('hour_project_id', (int) $filters['hour_project_id']);
        }

        return response()->streamDownload(function () use ($user, $query): void {
            $handle = fopen('php://output', 'w');
            if ($handle === false) {
                return;
            }

            fputcsv($handle, [
                'employee_id',
                'employee_name',
                'week_start',
                'registration_type',
                'target_label',
                'total_hours',
            ]);

            foreach ($query->orderByDesc('id')->lazy(100) as $line) {
                if (! $user->can('hours.admin') && ! $this->approverResolver->actingUserMayApprove($user, $line)) {
                    continue;
                }

                $week = $line->week;
                $employee = $week ? $week->user : null;
                $target = $line->registration_type->value.' #'.$line->getKey();

                $totalHours = $this->totalsCalculator->lineTotalHours($line);

                fputcsv($handle, [
                    $week !== null ? (int) $week->user_id : '',
                    $employee ? (string) $employee->name : '',
                    $week !== null ? $week->week_start->format('Y-m-d') : '',
                    $line->registration_type->value,
                    $target,
                    (string) $totalHours,
                ]);
            }

            fclose($handle);
        }, $filename, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }
}
