<?php

namespace App\Http\Controllers\Owner\Provider;

use App\Http\Controllers\Controller;
use App\Modules\Provider\Application\PassengerDistributionNationalityDetail\PassengerDistributionNationalityDetailQuery;
use App\Modules\Provider\Application\PassengerDistributionNationalityDetail\PassengerDistributionNationalityDetailQueryResponse;
use App\Modules\Provider\Application\PassengerDistributionToursDetail\PassengerDistributionToursDetailQuery;
use App\Modules\Provider\Application\PassengerDistributionToursDetail\PassengerDistributionToursDetailQueryResponse;
use App\Modules\Provider\Infrastructure\ProviderValidators;
use App\Modules\Shared\Domain\Bus\Query\QueryBus;
use App\Modules\Shared\Validator\Infraestructure\LaravelValidator;
use App\Providers\ProvidersDistribution\ProvidersDistributionDetail\ProvidersDistributionDetailReport;
use Carbon\CarbonPeriod;
use Illuminate\Http\Request;

class ProviderListPassengerDistributionDetailsReportController extends Controller
{
    public function __construct(
        private QueryBus $queryBus,
    )
    {
    }

    public function __invoke(Request $request, $Id_Provider)
    {
        (new LaravelValidator)->validate($request->all(), 'Provider', ProviderValidators::listPassengerDistribution());
        /** @var PassengerDistributionToursDetailQueryResponse $response */
        $response = $this->queryBus->ask(
            new PassengerDistributionToursDetailQuery(
                $request->input('Date_Start'),
                $request->input('Date_End'),
                $request->input('Id_TypeTour'),
                $Id_Provider,
                ''));

        $dataGroupedTours = $this->groupByMonthAndTours($response->data(), $request->input('Date_Start'), $request->input('Date_End'));
        $toursWithData = collect($dataGroupedTours)
            ->pluck('ToursWithData')
            ->flatten(1)
            ->unique('Code')
            ->values()
            ->toArray();

        /** @var PassengerDistributionNationalityDetailQueryResponse $response */
        $response = $this->queryBus->ask(
            new PassengerDistributionNationalityDetailQuery(
                $request->input('Date_Start'),
                $request->input('Date_End'),
                $Id_Provider,
                1,
                1000000));

        $dataGroupedNationality = $this->groupByMonthAndNationality($response->data(), $request->input('Date_Start'), $request->input('Date_End'));
        $countriesWithData = collect($dataGroupedNationality)
            ->pluck('CountriesWithData')
            ->flatten(1)
            ->unique('Code')
            ->values()
            ->toArray();

        $merged = $this->normalizeDailyData($dataGroupedTours, $dataGroupedNationality);

        $normalized = $this->normalizeDays(
            $merged,
            $toursWithData,
            $countriesWithData
        );
        (new ProvidersDistributionDetailReport())
            ->exportToExcel($normalized, $toursWithData, $countriesWithData);
    }

    private function normalizeDailyData($toursData, $countriesData)
    {
        $mapNationality = collect($countriesData)
            ->pluck('Days')
            ->flatten(1)
            ->keyBy('Date');

        $result = [];

        foreach ($toursData as $month) {

            foreach ($month['Days'] as &$day) {

                $date = $day['Date'];

                $day['Countries'] = $mapNationality[$date]['Countries'] ?? [];
                $day['Total_Day_Countries'] = $mapNationality[$date]['Total_Day_Countries'] ?? 0;
            }

            $result[] = $month;
        }

        return $result;
    }

    private function normalizeDays($data, $allTours, $allCountries)
    {
        foreach ($data as &$month) {

            $monthTotalsTours = [];
            $monthTotalsCountries = [];
            $totalTours = 0;
            $totalCountries = 0;

            foreach ($month['Days'] as &$day) {

                $toursMap = collect($day['Tours'])->keyBy('Tour_Code');
                $countriesMap = collect($day['Countries'])->keyBy('UserCountry_Code');

                $normalizedTours = [];
                $normalizedCountries = [];

                foreach ($allTours as $tour) {

                    $code = $tour['Code'];

                    $rawCount = $toursMap->get($code)['Count'] ?? 0;

                    $count = $rawCount ?: '';

                    $normalizedTours[] = [
                        'Code' => $code,
                        'Name' => $tour['Name'],
                        'Color' => $tour['Color'],
                        'Count' => $count
                    ];

                    $monthTotalsTours[$code] = ($monthTotalsTours[$code] ?? 0) + $rawCount;
                    $totalTours += $rawCount;
                }

                foreach ($allCountries as $country) {

                    $code = $country['Code'];

                    $rawCount = $countriesMap->get($code)['Count'] ?? 0;
                    $count = $rawCount ?: '';

                    $normalizedCountries[] = [
                        'Code' => $code,
                        'Name' => $country['Name'] ?? $code,
                        'Count' => $count
                    ];

                    $monthTotalsCountries[$code] = ($monthTotalsCountries[$code] ?? 0) + $rawCount;
                    $totalCountries += $rawCount;
                }

                $day['Tours'] = $normalizedTours;
                $day['Countries'] = $normalizedCountries;

                $day['Total_Day_Countries'] = $day['Total_Day_Countries'] ?? 0;
                $day['Total_Day_Tours'] = $day['Total_Day_Tours'] ?? 0;
            }

            $month['Totals'] = [
                'Tours' => array_map(fn($tour) => [
                    'Code' => $tour['Code'],
                    'Count' => ($monthTotalsTours[$tour['Code']] ?? 0) ?: ''
                ], $allTours),

                'Countries' => array_map(fn($country) => [
                    'Code' => $country['Code'],
                    'Count' => ($monthTotalsCountries[$country['Code']] ?? 0) ?: ''
                ], $allCountries),

                'Total_Tours' => $totalTours ?: '',
                'Total_Countries' => $totalCountries ?: '',
            ];
        }

        return $data;
    }

    private function groupByMonthAndNationality($rows, $dateStart, $dateEnd)
    {
        $months = [
            1 => 'Enero', 2 => 'Febrero', 3 => 'Marzo', 4 => 'Abril',
            5 => 'Mayo', 6 => 'Junio', 7 => 'Julio', 8 => 'Agosto',
            9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre'
        ];

        $dataMap = [];

        foreach ($rows as $row) {
            $dataMap[$row->date][] = [
                "Id_UserCountry" => $row->Id_UserCountry,
                "UserCountry_Name" => $row->UserCountry_Name,
                "UserCountry_Code" => $row->UserCountry_Code,
                "Count" => $row->count
            ];
        }

        $period = CarbonPeriod::create($dateStart, $dateEnd);

        $grouped = [];

        foreach ($period as $date) {

            $year = $date->year;
            $month = $date->month;
            $day = $date->day;
            $dateStr = $date->toDateString();

            $key = $year . '-' . $month;

            if (!isset($grouped[$key])) {
                $grouped[$key] = [
                    "Year" => $year,
                    "Id" => $key,
                    "Month" => $month,
                    "Month_Name" => $months[$month],
                    "Days" => [],
                    "CountriesWithData" => []
                ];
            }

            $countries = $dataMap[$dateStr] ?? [];

            $totalDay = array_sum(array_column($countries, 'Count'));

            foreach ($countries as $country) {
                $grouped[$key]["CountriesWithData"][$country["UserCountry_Code"]] = [
                    "Code" => $country["UserCountry_Code"],
                    "Name" => $country["UserCountry_Name"]
                ];
            }

            $grouped[$key]["Days"][] = [
                "Day" => $day,
                "Date" => $dateStr,
                "Total_Day_Countries" => $totalDay,
                "Countries" => $dataMap[$dateStr] ?? []
            ];
        }

        foreach ($grouped as &$month) {
            $month["CountriesWithData"] = array_values($month["CountriesWithData"]);
        }

        return array_values($grouped);
    }

    private function groupByMonthAndTours($rows, $dateStart, $dateEnd)
    {
        $months = [
            1 => 'Enero', 2 => 'Febrero', 3 => 'Marzo', 4 => 'Abril',
            5 => 'Mayo', 6 => 'Junio', 7 => 'Julio', 8 => 'Agosto',
            9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre'
        ];

        $dataMap = [];

        foreach ($rows as $row) {
            $dataMap[$row->date][] = [
                "Id_Tour" => $row->Id_Tour,
                "Tour_Code" => $row->Tour_Code,
                "Tour_Color" => $row->Tour_Color,
                "Count" => 1
            ];
        }

        $period = CarbonPeriod::create($dateStart, $dateEnd);

        $grouped = [];

        foreach ($period as $date) {

            $year = $date->year;
            $month = $date->month;
            $day = $date->day;
            $dateStr = $date->toDateString();

            $key = $year . '-' . $month;

            if (!isset($grouped[$key])) {
                $grouped[$key] = [
                    "Year" => $year,
                    "Id" => $key,
                    "Month" => $month,
                    "Month_Name" => $months[$month],
                    "Days" => [],
                    "ToursWithData" => []
                ];
            }

            $tours = $dataMap[$dateStr] ?? [];

            $totalDay = array_sum(array_column($tours, 'Count'));

            foreach ($tours as $tour) {
                $grouped[$key]["ToursWithData"][$tour["Tour_Code"]] = [
                    "Code" => $tour["Tour_Code"],
                    "Name" => $tour["Tour_Code"],
                    "Color" => $tour["Tour_Color"] ?? null
                ];
            }

            $grouped[$key]["Days"][] = [
                "Day" => $day,
                "Date" => $dateStr,
                "Total_Day_Tours" => $totalDay,
                "Tours" => $dataMap[$dateStr] ?? []
            ];
        }

        foreach ($grouped as &$month) {
            $month["ToursWithData"] = array_values($month["ToursWithData"]);
        }

        return array_values($grouped);
    }
}
