<?php

namespace App\Exports;


use App\Models\PointPlan;
use App\Services\TrafficReportCalculator;
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\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use Carbon\Carbon;

class TrafficDetailedReportExport implements FromArray, WithHeadings, WithEvents
{
    /** @var Collection|PointPlan[] */
    protected Collection|array $plans;

    /**
     * @var array строки, где стоит название района
     */
    protected array $districtRows = [];

    /**
     * @var string Название выбранного района или пусто для области
     */
    protected string $districtName;

    /**
     * @var array
     */
    protected array $mergeRows = [];

    /**
     * @var int номер строки, где ставим подпись
     */
    protected int $signatureRow;
    protected int $lineRow;
    protected int $fio;
    protected string $managerName;
    protected Carbon $reportDate;

    public function __construct($plans, string $districtName = '', string $managerName = '')
    {
        // превращаем любой вход в коллекцию
        $this->plans = collect($plans);
        $this->districtName = $districtName;
        $this->managerName  = $managerName;
        // если дат нет — берём «сегодня»
        $this->reportDate = $this->plans->first()->day
            ?? now();
    }

    public function headings(): array
    {
        $period = $this->monthYearLabel();

        return [
            ['Отчет о среднегодовой суточной интенсивности движения по типам транспортных средств на автомобильных дорогах общего пользования','','','','','','','','','','','','','','','','','','','','','','',''],
            ["регионального и межмуниципального значения Оренбургской области за {$period}.",'','','','','','','','','','','','','','','','','','','','','','',''],
            ['по государственному контракту № 14/02-84 от 23.08.2022 и договору № 20-13/8с-КСМП25 от 14.04.2025.','','','','','','','','','','','','','','','','','','','','','','',''],
            // 4-я строка
            [
                'Место учёта, км',
                'Границы перегона от, км', '',
                'Протяженность перегона, км',
                'Количество легких автотранспортных средств, шт./сут', '',
                'Количество тяжелых автотранспортных средств, шт/сут', '', '', '', '', '', '', '', '', '', '', '',
                'Всего транспортных средств, шт/сут',
                'Всего транспортных средств, приведенных к легковому автомобилю, единиц в сутки',
                'Максимальная интенсивность за год', '', '', '',
            ],
            [
                '', 'от',
                'до', '',
                'A',
                'B',
                'C', '', '', '', '', '', '', '', '', '', '',
                'D', '', '',
                'Часовая, шт./ч',
                'То же, приведенных к легковому автомобилю, единиц в час',
                'Наибольшая часовая, повторяющаяся в течении не менее 50 ч в год, шт./ч',
                'Суточная, шт./сут',
            ],
            [
                '', '', '', '', 'Мотоциклы',
                'Легковые автомобили, небольшие грузовики (фургоны) и другие автомобили с прицепом и без него',
                'Двухосные грузовые автомобили',
                'Трехосные грузовые автомобили',
                'Четырехосные грузовые автомобили',
                'Четырехосные автопоезда (двухосный грузовой автомобиль с прицепом)',
                'Пятиосные автопоезда (трехосный грузовой автомобиль с прицепом)',
                'Трехосные седельные автопоезда (двухосный седельный тягач с полуприцепом)',
                'Четырехосные седельные автопоезда (двухосный седельный тягач с прицепом)',
                'Пятиосные седельные автопоезда (двухосный седельный тягач с прицепом)',
                'Пятиосные седельные автопоезда (трехосный седельный тягач с полуприцепом)',
                'Шестиосные седельные автопоезда',
                'Автомобили с семью и более осями и другие',
                'Автобусы', '', '', '', '', '', '',
            ],
            [
                '1',
                '2',
                '3',
                '4',
                '5',
                '6',
                '7',
                '8',
                '9',
                '10',
                '11',
                '12',
                '13',
                '14',
                '15',
                '16',
                '17',
                '18',
                '19',
                '20',
                '21',
                '22',
                '23',
                '24',
            ],
        ];
    }

    public function registerEvents(): array
    {
        return [
            // после того как лист создан, объединим нужные ячейки
            AfterSheet::class => function (AfterSheet $event) {
                $sheet = $event->sheet->getDelegate();
                $lastCol = $sheet->getHighestColumn();
                $lastRow = $sheet->getHighestRow();
                $fullArea = "A4:{$lastCol}{$lastRow}";

                $sheet->getStyle($fullArea)->applyFromArray([
                    'borders' => [
                        'allBorders' => ['borderStyle' => Border::BORDER_THIN],
                    ],
                    'alignment' => [
                        'horizontal' => Alignment::HORIZONTAL_CENTER,
                        'vertical' => Alignment::VERTICAL_CENTER,
                        'wrapText' => true,
                    ],
                ]);

                /* ---------- объединяем строки районов и точек ---------- */
                foreach ($this->mergeRows as $row) {
                    $sheet->mergeCells("A{$row}:X{$row}");
                    $sheet->getStyle("A{$row}:X{$row}")
                        ->getFont()->setBold(true);
                }

                /* ---------- серый фон районам ---------- */
                foreach ($this->districtRows as $row) {       // +++
                    $sheet->getStyle("A{$row}:X$row")
                        ->getFill()
                        ->setFillType(Fill::FILL_SOLID)
                        ->getStartColor()->setARGB('FFD9D9D9');   // светло-серый
                }



                $sheet->mergeCells("A1:{$lastCol}1");
                $sheet->mergeCells("A2:{$lastCol}2");
                $sheet->mergeCells("A3:{$lastCol}3");

                $sheet->getStyle("A1:{$lastCol}3")
                    ->applyFromArray([
                        'alignment' => [
                            'horizontal' => Alignment::HORIZONTAL_CENTER,
                            'vertical'   => Alignment::VERTICAL_CENTER,
                        ],
                        'font' => ['bold' => true],
                    ]);

                // 1) Место учёта: A1:A3
                $sheet->mergeCells('A4:A6');

                // 2) Границы перегона: B1:C1
                $sheet->mergeCells('B4:C4');
                // оставляем B2="от", C2="до"
                // от
                $sheet->mergeCells('B5:B6');
                // до
                $sheet->mergeCells('C5:C6');

                // 3) Протяженность: D1:D3
                $sheet->mergeCells('D4:D6');
                // 4) Количество: E1:F1
                $sheet->mergeCells('E4:F4');
                // 5) Количество тяжелых: G1:R1
                $sheet->mergeCells('G4:R4');
                // 6) Всего: S1:S3
                $sheet->mergeCells('S4:S6');
                // 7) Всего к легковым: T1:T3
                $sheet->mergeCells('T4:T6');
                // 8) Максимальная интенсивность: U1:X1
                $sheet->mergeCells('U4:X4');
                // 9) С: G2:Q2
                $sheet->mergeCells('G5:Q5');
                // 10) Часовая: U2:U3
                $sheet->mergeCells('U5:U6');
                // 11) Приведенная: V2:V3
                $sheet->mergeCells('V5:V6');
                // 11) Наибольшая: W2:W3
                $sheet->mergeCells('W5:W6');
                // 11) Приведенная: V2:V3
                $sheet->mergeCells('X5:X6');

                $r = $this->signatureRow;
                if ($this->managerName === '') {
                    // 1) Вставляем печать
                    $stamp = new Drawing();
                    $stamp->setName('Печать');
                    $stamp->setPath(storage_path('app/public/images/stamp.png')); // путь к вашему файлу печати
                    $stamp->setHeight(130);    // подберите размер
                    $stamp->setOffsetX(0);    // сдвиг внутри ячейки
                    $stamp->setOffsetY(-5);
                    $stamp->setCoordinates("C{$r}");  // ячейка, в которой будет центр печати
                    $stamp->setWorksheet($sheet);

                    // 2) Вставляем подпись
                    $sign = new Drawing();
                    $sign->setName('Подпись');
                    $sign->setPath(storage_path('app/public/images/signature.png')); // путь к вашей подписи
                    $sign->setHeight(60);
                    $sign->setOffsetX(0);
                    $sign->setOffsetY(0);
                    $sign->setCoordinates("B{$r}");  // ячейка, где должна быть подпись
                    $sign->setWorksheet($sheet);
                }

                $sheet->mergeCells("A{$r}:E{$r}");
                $sheet->mergeCells("J{$r}:O{$r}");
                $sheet->mergeCells("T{$r}:X{$r}");


                $emptyRow = $r - 1;
                $noBorderRange1 = "A{$emptyRow}:{$lastCol}{$emptyRow}";
                $noBorderRange2 = "A{$r}:{$lastCol}{$r}";

                // задаём для этих диапазонов borderStyle NONE
                $sheet->getStyle($noBorderRange1)->applyFromArray([
                    'borders' => [
                        'allBorders' => ['borderStyle' => Border::BORDER_NONE],
                    ],
                ]);
                $sheet->getStyle($noBorderRange2)->applyFromArray([
                    'borders' => [
                        'allBorders' => ['borderStyle' => Border::BORDER_NONE],
                    ],
                ]);

                $lr = $this->lineRow;
                // A–E
                $sheet->getStyle("A{$lr}:X{$lr}")
                    ->applyFromArray([
                        'borders' => [
                            'allBorders' => ['borderStyle' => Border::BORDER_NONE],
                        ],
                    ]);
                $sheet->getStyle("A{$lr}:E{$lr}")
                    ->applyFromArray([
                        'borders' => [
                            'bottom' => ['borderStyle' => Border::BORDER_THIN],
                        ],
                    ]);
                // K–O
                $sheet->getStyle("J{$lr}:O{$lr}")
                    ->applyFromArray([
                        'borders' => [
                            'bottom' => ['borderStyle' => Border::BORDER_THIN],
                        ],
                    ]);
                // T–X
                $sheet->getStyle("T{$lr}:X{$lr}")
                    ->applyFromArray([
                        'borders' => [
                            'bottom' => ['borderStyle' => Border::BORDER_THIN],
                        ],
                    ]);


                $fl = $this->fio;
                $sheet->mergeCells("A{$fl}:E{$fl}");
                $sheet->getStyle("A{$fl}:X{$fl}")
                    ->applyFromArray([
                        'borders' => [
                            'allBorders' => ['borderStyle' => Border::BORDER_NONE],
                        ],
                    ]);

                if ($this->districtName === ''){
                    $sheet->mergeCells("J{$fl}:O{$fl}");
                }else {
                    $sheet->getStyle("J{$fl}:O{$fl}")
                        ->applyFromArray([
                            'borders' => [
                                'bottom' => ['borderStyle' => Border::BORDER_THIN],
                            ],
                        ]);
                }
                $sheet->getStyle("T{$fl}:X{$fl}")
                    ->applyFromArray([
                        'borders' => [
                            'bottom' => ['borderStyle' => Border::BORDER_THIN],
                        ],
                    ]);


                // 5) Стиль: жирный и выравнивание по центру
                $sheet->getStyle('A1:X4')->getFont()->setBold(true);
                $sheet->freezePane('A8');
                $setup = $sheet->getPageSetup();
                $setup->setPaperSize(PageSetup::PAPERSIZE_A4);
                $setup->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
                $setup->setFitToWidth(1);     // ужать по ширине в одну страницу
                $setup->setFitToHeight(0);    // по высоте — сколько понадобится
                $sheet->getSheetView()->setShowZeros(true);

            },
        ];
    }


    public function array(): array
    {
        $rows = [];
        $headerRows = count($this->headings());                       // 4
        $currentRow = $headerRows + 1;                                // счёт строк в итоге

        /* 1. группируем по району и сортируем */
        $grouped = $this->plans
            ->sortBy(fn(PointPlan $p) => mb_strtolower(
                $p->point?->district?->name ?? 'Без района'
            ))
            ->groupBy(fn(PointPlan $p) => $p->point?->district?->name ?? 'Без района');

        /* 2. идём по районам */
        foreach ($grouped as $districtName => $plans) {
            /* вставляем строку-заголовок района */
            $rows[] = array_merge([$districtName], array_fill(0, 23, ''));
            $this->mergeRows[] = $currentRow;
            $this->districtRows[] = $currentRow;
            $currentRow++;

            // Группируем внутри района по точке!
            /** @var \Illuminate\Support\Collection $plans */
            $pointsGrouped = $plans->groupBy(fn(PointPlan $p) => $p->point_id);

            /* 3. точки внутри района */
            foreach ($pointsGrouped as $pointId => $pointPlans) {
                $point = $pointPlans->first()->point;
                $pointLabel = trim("{$point->record_number} {$point->name}");
                /* строка-заголовок точки */
                $rows[] = array_merge([$pointLabel], array_fill(0, 23, ''));
                $this->mergeRows[] = $currentRow;                     // ★ запомнили
                $currentRow++;

                // Агрегация значений по pointPlans
                $rows[] = $this->pointDataRowPeriod($pointPlans);
                $currentRow++;
            }
        }

        $rows[] = array_fill(0, 24, '');
        $currentRow++;
        if ($this->districtName === '') {
            $rows[] = array_merge(
                [
                    'Генеральный директор ООО "ИТБ"', '', '', '', '',
                    '', '', '', '',
                    'Начальник отдела ПТО АО "Оренбургремдорстрой"', '', '', '', '',
                    '', '', '', '', '',
                    'Представитель ГУ ГУДХОО', '', '', '', '',
                ]
            );
        } else {
            $rows[] = array_merge(
                [
                    'Районный менеджер ООО "ИТБ"', '', '', '', '',
                    '', '', '', '',
                    'Представитель АО "Оренбургремдорстрой"', '', '', '', '',
                    '', '', '', '', '',
                    'Представитель ГУ ГУДХОО', '', '', '', '',
                ]
            );
        }

        $this->signatureRow = $currentRow;
        $currentRow++;

        // 3) ещё одна строка — линия для росписи
        $rows[] = array_fill(0, 24, '');
        $this->lineRow = $currentRow;
        $currentRow++;
        if ($this->districtName === '') {
            $rows[] = array_merge(
                [
                    'Иванов Дмитрий Александрович', '', '', '', '',
                    '', '', '', '',
                    'Зайцева Ксения Петровна', '', '', '', '',
                    '', '', '', '',
                    '', '', '', '', '',
                ]
            );
        } else {
            $rows[] = array_merge(
                [
                    "$this->managerName", '', '', '', '',
                    '', '', '', '', '',
                    '', '', '', '', '',
                    '', '', '', '',
                    '', '', '', '', '',
                ]
            );
        }



        $this->fio = $currentRow;

        return $rows;
    }

    protected function pointDataRow(PointPlan $plan): array           // ★ new
    {
        $cats = [
            'bike', 'car', 'truck2', 'truck3', 'truck4', 'roadtrain4', 'roadtrain5',
            'saddleroadtrain3', 'saddleroadtrain4', 'saddleroadtrain23',
            'saddleroadtrain32', 'saddleroadtrain33', 'truck7', 'bus',
        ];

        $point = $plan->point;
        $calc = (new TrafficReportCalculator($plan))->calculate();

        $dailyByCat = [];
        foreach ($cats as $key) {
            $dailyByCat[] = (int)round($calc['detail'][$key]['Ncs'] ?? 0);
        }

        $totalDaily = (int)round($calc['totalNcs'] ?? 0);
        $totalCarEq = (int)round($calc['totalCarEq'] ?? 0);

        $hourly = (int)round($totalDaily / 24);
        $hourlyEq = (int)round($totalCarEq / 24);
        $peak50 = (int)round($calc['meta']['peak50'] ?? $hourly);

        $accounting_point = $this->parseKm($point->accounting_point);
        $length = (float)$point->length;


        $kmFrom = $point->segment_from !== null
            ? $point->segment_from
            : 0;
        $kmTo = $point->segment_to !== null
            ? $point->segment_to
            : $kmFrom + (float)$point->length;

        /* 4. собираем строку */
        return array_merge(
            [$accounting_point, $kmFrom, $kmTo, $length],
            $dailyByCat,
            [$totalDaily, $totalCarEq, $hourly, $hourlyEq, $peak50, $totalDaily] // TODO $peak50 пересчитать за год. И Суточную максимальную за год пересчитать
        );
    }

    protected function pointDataRowPeriod(Collection $plans): array
    {
        // Берём первую запись для общих данных точки
        $plan = $plans->first();
        $point = $plan->point;

        $cats = [
            'bike', 'car', 'truck2', 'truck3', 'truck4', 'roadtrain4', 'roadtrain5',
            'saddleroadtrain3', 'saddleroadtrain4', 'saddleroadtrain23',
            'saddleroadtrain32', 'saddleroadtrain33', 'truck7', 'bus',
        ];

        // Суммируем по категориям
        $dailyByCat = array_fill(0, count($cats), 0);
        $totalDaily = 0;
        $totalCarEq = 0;
        $peak50Sum = 0;
        $peak50Count = 0;
        $measureCount = 0;

        foreach ($plans as $planItem) {
            $calc = (new TrafficReportCalculator($planItem))->calculate();

            foreach ($cats as $i => $key) {
                $dailyByCat[$i] += (int)round($calc['detail'][$key]['Ncs'] ?? 0);
            }

            $totalDaily += (int)round($calc['totalNcs'] ?? 0);
            $totalCarEq += (int)round($calc['totalCarEq'] ?? 0);

            // Для peak50 — обычно надо среднее, если нет — просто сумму
            if (!empty($calc['meta']['peak50'])) {
                $peak50Sum += $calc['meta']['peak50'];
                $peak50Count++;
            }
            $measureCount++;
        }

        // Если есть хотя бы 1 замер — делим суммы на их количество
        if ($measureCount > 0) {
            $dailyByCat = array_map(fn($sum) => (int)round($sum / $measureCount), $dailyByCat);
            $totalDaily = (int)round($totalDaily / $measureCount);
            $totalCarEq = (int)round($totalCarEq / $measureCount);
            $hourly = (int)round($totalDaily / 24);
            $hourlyEq = (int)round($totalCarEq / 24);
            $peak50 = $peak50Count ? (int)round($peak50Sum / $peak50Count) : 0;
        } else {
            // Если нет замеров — всё по нулям
            $hourly = 0;
            $hourlyEq = 0;
            $peak50 = 0;
        }

        $accounting_point = $this->parseKm($point->accounting_point);
        $length = (float)$point->length;

        $kmFrom = $point->segment_from !== null
            ? $point->segment_from
            : 0;
        $kmTo = $point->segment_to !== null
            ? $point->segment_to
            : $kmFrom + (float)$point->length;

        return array_merge(
            [$accounting_point, $kmFrom, $kmTo, $length],
            $dailyByCat,
            [$totalDaily, $totalCarEq, $hourly, $hourlyEq, $peak50, $totalDaily]
        );
    }

    // внизу класса
    protected function parseKm(string|int|float $raw): float
    {
        $s = (string)$raw;
        if (str_contains($s, '+')) {
            $s = str_replace('+', '.', $s);
        }
        return (float)$s;
    }

    /** отдаём «июль 2025 г.» */
    protected function monthYearLabel(): string
    {
        // русские названия месяцев в винительном падеже («за июль»)
        $rusMonths = [
            1 => 'январь',   2 => 'февраль', 3 => 'март',
            4 => 'апрель',  5 => 'май',     6 => 'июнь',
            7 => 'июль',    8 => 'август',  9 => 'сентябрь',
            10 => 'октябрь',11 => 'ноябрь', 12 => 'декабрь',
        ];

        $m = (int) $this->reportDate->month;
        $y =        $this->reportDate->year;

        return "{$rusMonths[$m]} {$y} г";
    }
}
