<?php

declare(strict_types=1);

namespace App\Module\TimeTracking\Infrastructure\Export;

use App\Module\TimeTracking\Domain\Entity\TimeEntry;
use App\Shared\Domain\Contract\ProjectInterface;
use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

use function count;

class TimeEntryExportService
{
    private const array DETAIL_HEADERS = [
        'Date', 'Utilisateur', 'Projet', 'Tâche', 'Titre',
        'Tags', 'Début', 'Fin', 'Durée (h)', 'Description',
    ];

    private const array MONTH_NAMES = [
        1 => 'Janvier', 2 => 'Février', 3 => 'Mars', 4 => 'Avril',
        5 => 'Mai', 6 => 'Juin', 7 => 'Juillet', 8 => 'Août',
        9 => 'Septembre', 10 => 'Octobre', 11 => 'Novembre', 12 => 'Décembre',
    ];

    /**
     * @param TimeEntry[] $timeEntries
     *
     * @return string Path to the generated temp file
     */
    public function generate(array $timeEntries, DateTimeImmutable $from, DateTimeImmutable $to): string
    {
        $spreadsheet = new Spreadsheet();

        $this->buildDetailSheet($spreadsheet, $timeEntries);
        $this->buildProjectRecapSheet($spreadsheet, $timeEntries);
        $this->buildMonthRecapSheet($spreadsheet, $timeEntries, $from, $to);

        $spreadsheet->setActiveSheetIndex(0);

        $tempFile = tempnam(sys_get_temp_dir(), 'export_temps_').'.xlsx';
        $writer   = new Xlsx($spreadsheet);
        $writer->save($tempFile);

        return $tempFile;
    }

    /**
     * @param TimeEntry[] $timeEntries
     */
    private function buildDetailSheet(Spreadsheet $spreadsheet, array $timeEntries): void
    {
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setTitle('Détail');

        // Headers
        foreach (self::DETAIL_HEADERS as $col => $header) {
            $colLetter = Coordinate::stringFromColumnIndex($col + 1);
            $sheet->setCellValue("{$colLetter}1", $header);
        }
        $this->boldRow($sheet, 1, count(self::DETAIL_HEADERS));

        // Data rows
        $row = 2;
        foreach ($timeEntries as $entry) {
            $duration  = $this->computeDuration($entry);
            $task      = $entry->getTask();
            $taskLabel = '';
            if (null !== $task) {
                /** @var null|ProjectInterface $project */
                $project   = $task->getProject();
                $code      = $project?->getCode() ?? '';
                $taskLabel = $code.'-'.$task->getNumber().' - '.$task->getTitle();
            }

            $tagLabels = $entry->getTags()->map(fn ($t) => $t->getLabel() ?? '')->toArray();

            $sheet->setCellValue("A{$row}", $entry->getStartedAt()->format('Y-m-d'));
            $sheet->setCellValue("B{$row}", $entry->getUser()?->getUsername() ?? '');
            $sheet->setCellValue("C{$row}", $entry->getProject()?->getName() ?? '');
            $sheet->setCellValue("D{$row}", $taskLabel);
            $sheet->setCellValue("E{$row}", $entry->getTitle() ?? '');
            $sheet->setCellValue("F{$row}", implode(', ', $tagLabels));
            $sheet->setCellValue("G{$row}", $entry->getStartedAt()->format('H:i'));
            $sheet->setCellValue("H{$row}", $entry->getStoppedAt()?->format('H:i') ?? '');
            $sheet->setCellValue("I{$row}", round($duration, 2));
            $sheet->setCellValue("J{$row}", $entry->getDescription() ?? '');

            ++$row;
        }

        // Total row
        if ($row > 2) {
            $sheet->setCellValue("H{$row}", 'Total');
            $sheet->getStyle("H{$row}")->getFont()->setBold(true);
            $sheet->setCellValue("I{$row}", '=SUM(I2:I'.($row - 1).')');
            $sheet->getStyle("I{$row}")->getFont()->setBold(true);
        }

        // Auto-size columns
        foreach (range('A', 'J') as $col) {
            $sheet->getColumnDimension($col)->setAutoSize(true);
        }
    }

    /**
     * @param TimeEntry[] $timeEntries
     */
    private function buildProjectRecapSheet(Spreadsheet $spreadsheet, array $timeEntries): void
    {
        $sheet = $spreadsheet->createSheet();
        $sheet->setTitle('Récap par projet');

        // Aggregate: user → project → hours
        $data     = [];
        $projects = [];
        $users    = [];

        foreach ($timeEntries as $entry) {
            $userName    = $entry->getUser()?->getUsername() ?? 'Inconnu';
            $projectName = $entry->getProject()?->getName() ?? 'Sans projet';
            $duration    = $this->computeDuration($entry);

            $users[$userName]              = true;
            $projects[$projectName]        = true;
            $data[$userName][$projectName] = ($data[$userName][$projectName] ?? 0) + $duration;
        }

        ksort($users);
        ksort($projects);
        $projectList = array_keys($projects);
        $userList    = array_keys($users);

        // Headers
        $sheet->setCellValue('A1', 'Utilisateur');
        $col = 2;
        foreach ($projectList as $project) {
            $letter = Coordinate::stringFromColumnIndex($col);
            $sheet->setCellValue("{$letter}1", $project);
            ++$col;
        }
        $totalLetter = Coordinate::stringFromColumnIndex($col);
        $sheet->setCellValue("{$totalLetter}1", 'Total');
        $this->boldRow($sheet, 1, $col);

        // Data rows
        $row = 2;
        foreach ($userList as $user) {
            $sheet->setCellValue("A{$row}", $user);
            $col       = 2;
            $userTotal = 0;
            foreach ($projectList as $project) {
                $val    = round($data[$user][$project] ?? 0, 2);
                $letter = Coordinate::stringFromColumnIndex($col);
                $sheet->setCellValue("{$letter}{$row}", $val);
                $userTotal += $val;
                ++$col;
            }
            $letter = Coordinate::stringFromColumnIndex($col);
            $sheet->setCellValue("{$letter}{$row}", round($userTotal, 2));
            $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);
            ++$row;
        }

        // Total row
        $sheet->setCellValue("A{$row}", 'Total');
        $sheet->getStyle("A{$row}")->getFont()->setBold(true);
        $col = 2;
        foreach ($projectList as $project) {
            $projectTotal = 0;
            foreach ($userList as $user) {
                $projectTotal += $data[$user][$project] ?? 0;
            }
            $letter = Coordinate::stringFromColumnIndex($col);
            $sheet->setCellValue("{$letter}{$row}", round($projectTotal, 2));
            $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);
            ++$col;
        }
        // Grand total
        $grandTotal = 0;
        foreach ($data as $userData) {
            foreach ($userData as $hours) {
                $grandTotal += $hours;
            }
        }
        $letter = Coordinate::stringFromColumnIndex($col);
        $sheet->setCellValue("{$letter}{$row}", round($grandTotal, 2));
        $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);

        // Auto-size
        for ($c = 1; $c <= $col; ++$c) {
            $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
        }
    }

    /**
     * @param TimeEntry[] $timeEntries
     */
    private function buildMonthRecapSheet(Spreadsheet $spreadsheet, array $timeEntries, DateTimeImmutable $from, DateTimeImmutable $to): void
    {
        $sheet = $spreadsheet->createSheet();
        $sheet->setTitle('Récap par mois');

        // Build month columns from the date range
        $months  = [];
        $current = $from->modify('first day of this month');
        $end     = $to->modify('first day of this month');
        while ($current <= $end) {
            $key          = $current->format('Y-m');
            $label        = self::MONTH_NAMES[(int) $current->format('n')].' '.$current->format('Y');
            $months[$key] = $label;
            $current      = $current->modify('+1 month');
        }

        // Aggregate: user → month-key → hours
        $data  = [];
        $users = [];

        foreach ($timeEntries as $entry) {
            $userName = $entry->getUser()?->getUsername() ?? 'Inconnu';
            $monthKey = $entry->getStartedAt()->format('Y-m');
            $duration = $this->computeDuration($entry);

            $users[$userName]           = true;
            $data[$userName][$monthKey] = ($data[$userName][$monthKey] ?? 0) + $duration;
        }

        ksort($users);
        $userList  = array_keys($users);
        $monthKeys = array_keys($months);

        // Headers
        $sheet->setCellValue('A1', 'Utilisateur');
        $col = 2;
        foreach ($months as $label) {
            $letter = Coordinate::stringFromColumnIndex($col);
            $sheet->setCellValue("{$letter}1", $label);
            ++$col;
        }
        $totalLetter = Coordinate::stringFromColumnIndex($col);
        $sheet->setCellValue("{$totalLetter}1", 'Total');
        $this->boldRow($sheet, 1, $col);

        // Data rows
        $row = 2;
        foreach ($userList as $user) {
            $sheet->setCellValue("A{$row}", $user);
            $col       = 2;
            $userTotal = 0;
            foreach ($monthKeys as $monthKey) {
                $val    = round($data[$user][$monthKey] ?? 0, 2);
                $letter = Coordinate::stringFromColumnIndex($col);
                $sheet->setCellValue("{$letter}{$row}", $val);
                $userTotal += $val;
                ++$col;
            }
            $letter = Coordinate::stringFromColumnIndex($col);
            $sheet->setCellValue("{$letter}{$row}", round($userTotal, 2));
            $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);
            ++$row;
        }

        // Total row
        $sheet->setCellValue("A{$row}", 'Total');
        $sheet->getStyle("A{$row}")->getFont()->setBold(true);
        $col = 2;
        foreach ($monthKeys as $monthKey) {
            $monthTotal = 0;
            foreach ($userList as $user) {
                $monthTotal += $data[$user][$monthKey] ?? 0;
            }
            $letter = Coordinate::stringFromColumnIndex($col);
            $sheet->setCellValue("{$letter}{$row}", round($monthTotal, 2));
            $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);
            ++$col;
        }
        $grandTotal = 0;
        foreach ($data as $userData) {
            foreach ($userData as $hours) {
                $grandTotal += $hours;
            }
        }
        $letter = Coordinate::stringFromColumnIndex($col);
        $sheet->setCellValue("{$letter}{$row}", round($grandTotal, 2));
        $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);

        // Auto-size
        for ($c = 1; $c <= $col; ++$c) {
            $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($c))->setAutoSize(true);
        }
    }

    private function computeDuration(TimeEntry $entry): float
    {
        $start = $entry->getStartedAt();
        $end   = $entry->getStoppedAt();

        if (null === $start || null === $end) {
            return 0;
        }

        return ($end->getTimestamp() - $start->getTimestamp()) / 3600;
    }

    private function boldRow(Worksheet $sheet, int $row, int $colCount): void
    {
        for ($c = 1; $c <= $colCount; ++$c) {
            $letter = Coordinate::stringFromColumnIndex($c);
            $sheet->getStyle("{$letter}{$row}")->getFont()->setBold(true);
        }
    }
}
