<?php

namespace App\Exports;

use Carbon\Carbon;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Fill;


/**
 * Экспорт календарного план‑графика (утро/день) в Excel.
 *
 * @param Collection $points коллекция точек с подгруженными plans (как в getCalendarView)
 * @param array $calendar тот же массив месяцев, что приходит на фронт
 * @param int $year
 */
class ScheduleCalendarExport implements FromArray, WithHeadings, WithEvents
{
    public function __construct(
        protected Collection $points,
        protected array      $calendar,
        protected int        $year,
        protected int        $month,
    )
    {
    }

    /** @var array [['A5','B5'], …] – какие ячейки надо залить */
    protected array $toFill = [];

    /** @var array Номера строк с районами */
    protected array $districtRows = [];

    // --- Считаем общее число day-столбцов
    protected function getTotalDayCols(): int
    {
        return array_reduce($this->calendar, fn($sum, $m) => $sum + count($m['days']) * 2, 0);
    }

    /* ---------- 1. Шапка ------------------------------------------------ */

    public function headings(): array
    {
        // первая строка: «Пункт учета …» + месяцы
        $rowMonth = ['№ ПУИДД', 'Название а/д', 'Координаты'];
        $rowDate = ['', '', ''];
        foreach ($this->calendar as $m) {
            $span = count($m['days']) * 2;
            $rowMonth = array_merge(
                $rowMonth,
                [$m['month'] . ' ' . $this->year],
                array_fill(0, $span - 1, '')
            );                               // вторая половинка

            foreach ($m['days'] as $d) {
                // две ячейки под один день
                $rowDate[] = $d;
                $rowDate[] = '';
            }
        }

        return [$rowMonth, $rowDate];
    }

    /* ---------- 2. Данные ---------------------------------------------- */

    public function array(): array
    {
        $rows = [];
        $rowIndex = 3;

        // Группировка точек по району (district)
        $pointsByDistrict = $this->points
            ->sortBy(fn($p) => $p->district->name)
            ->groupBy(fn($p) => $p->district->name);

        foreach ($pointsByDistrict as $districtName => $points) {
            // --- ВСТАВЛЯЕМ СТРОКУ-РАЗДЕЛИТЕЛЬ (район) ---
            $districtRow = array_merge(
                [$districtName, '', ''],
                array_fill(0, $this->getTotalDayCols(), '')
            );
            $rows[] = $districtRow;
            $this->districtRows[] = $rowIndex; // запоминаем строку для стилей
            $rowIndex++;
            foreach ($points as $p) {
                $row = [
                    $p->record_number,
                    $p->name,
                    $p->latitude && $p->longitude
                        ? "{$p->latitude}, {$p->longitude}"
                        : '-',
                ];

                $colIndex = 4;
                foreach ($this->calendar as $mi => $m) {
                    foreach ($m['days'] as $d) {
                        $date = Carbon::create($this->year, $this->month, $d)->toDateString();
                        $dayPlans = $p->plans->filter(
                            fn($pl) => Carbon::parse($pl->day)->toDateString() === $date
                        );

                        $hasMorning = $dayPlans->contains(
                            fn($pl) => Carbon::parse($pl->timeStart)->hour < 12
                        );
                        $hasAfternoon = $dayPlans->contains(
                            fn($pl) => Carbon::parse($pl->timeStart)->hour >= 12
                        );

                        // пишем пусто, но запоминаем координаты на заливку
                        $row[] = ''; // AM‑ячейка
                        if ($hasMorning) {
                            $this->toFill[] = Coordinate::stringFromColumnIndex($colIndex) . $rowIndex;
                        }
                        $colIndex++;

                        $row[] = ''; // PM‑ячейка
                        if ($hasAfternoon) {
                            $this->toFill[] = Coordinate::stringFromColumnIndex($colIndex) . $rowIndex;
                        }
                        $colIndex++;
                    }
                }

                $rows[] = $row;
                $rowIndex++;
            }
        }
        return $rows;
    }

    /* ---------- 3. Стили / объединения --------------------------------- */

    public function registerEvents(): array
    {
        return [
            AfterSheet::class => function (AfterSheet $e) {

                $sheet = $e->sheet->getDelegate();
                $totalDays = array_reduce($this->calendar, fn($sum,$m) => $sum + count($m['days']), 0);
                $maxColI   = 3 + $totalDays * 2;
                $lastCol   = Coordinate::stringFromColumnIndex($maxColI);
                $lastRow = $sheet->getHighestRow();
                /* рамка + центровка */
                $sheet->getStyle("A1:{$lastCol}{$lastRow}")
                    ->applyFromArray([
                        'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
                        'alignment' => [
                            'horizontal' => Alignment::HORIZONTAL_CENTER,
                            'vertical' => Alignment::VERTICAL_CENTER,
                            'wrapText' => true,
                        ],
                    ]);

                /* объединяем: месяц (строка 1) и дату (строка 2) */
                $col = 4;                               // A=1,B=2,C=3,D=4
                foreach ($this->calendar as $m) {
                    $span = count($m['days']) * 2;        // 2 ячейки на день

                    // месяц
                    $sheet->mergeCellsByColumnAndRow($col, 1, $col + $span - 1, 1);

                    // каждая дата
                    for ($d = 0; $d < $span; $d += 2) {
                        $sheet->mergeCellsByColumnAndRow($col + $d, 2, $col + $d + 1, 2);
                    }
                    $col += $span;
                }

                /* ширины */
                $sheet->getColumnDimension('A')->setWidth(10);
                $sheet->getColumnDimension('B')->setWidth(32);
                $sheet->getColumnDimension('C')->setWidth(16);
                for ($c = 4; $c <= $maxColI; $c++) {
                    $sheet->getColumnDimensionByColumn($c)->setWidth(3);
                }

                /* фиксируем 2строки шапки и 3 первых колонки */
                $sheet->freezePane('D3');

                $sheet = $e->sheet->getDelegate();
                foreach ($this->toFill as $coord) {
                    $sheet
                        ->getStyle($coord)
                        ->getFill()
                        ->setFillType(Fill::FILL_SOLID)
                        ->getStartColor()
                        ->setARGB('FF007BFF');
                }

                // Стилизация и объединение строк с районами
                foreach ($this->districtRows as $rowIdx) {
                    $mergeRange = "A{$rowIdx}:{$lastCol}{$rowIdx}";
                    $sheet->mergeCells($mergeRange);
                    $sheet->getStyle($mergeRange)
                        ->applyFromArray([
                            'font' => ['bold' => true, 'size' => 13],
                            'alignment' => [
                                'horizontal' => Alignment::HORIZONTAL_CENTER,
                                'vertical' => Alignment::VERTICAL_CENTER,
                            ],
                            'fill' => [
                                'fillType' => Fill::FILL_SOLID,
                                'startColor' => ['argb' => 'FFE2E3E5'],
                            ],
                        ]);
                }
            }
        ];
    }
}
