import { Component, OnInit } from "@angular/core";
import { RouterLink, RouterModule } from "@angular/router";
import { formatDate } from "@angular/common";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatListModule } from "@angular/material/list";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatRadioModule } from "@angular/material/radio";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { FormsModule } from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";

import { ChartConfiguration, ChartData, ChartType } from "chart.js";
import { from, groupBy, mergeMap, reduce, map } from "rxjs";
import { BaseChartDirective } from "ng2-charts";

import { ServiceComm } from "../../services/service-comm.service";
import { TunnelFlightService } from "../../services/tunnel-flight.service";
import { DateService } from "../../services/date.service";
import { TunnelFlight, TunnelFlightByMonth } from "../../models/tunnel-flight";

@Component({
    selector: "app-list-of-tunnel-flights",
    templateUrl: "./list-of-tunnel-flights.component.html",
    styleUrls: ["./list-of-tunnel-flights.component.css"],
    imports: [
        TranslateModule,
        BaseChartDirective,
        CommonModule,
        MatIconModule,
        MatListModule,
        MatProgressSpinnerModule,
        RouterLink,
        MatRadioModule,
        MatProgressBarModule,
        FormsModule,
        RouterModule,
        MatTableModule,
        MatButtonModule,
    ],
})
export class ListOfTunnelFlightsComponent implements OnInit {
    public barChartLegend = true;
    public barChartData: ChartData<"bar">;
    public barChartOptions: ChartConfiguration["options"];
    public barChartType: ChartType;
    public isLoading: boolean = false;
    public selectedPeriod: string;
    public dataSourceTable: MatTableDataSource<TunnelFlight> =
        new MatTableDataSource();
    public displayedColumns: Array<string> = [
        "id",
        "tunnel",
        "jumpType",
        "nbMinutes",
        "notes",
        "flightDate",
        "actions",
    ];
    public stats: Array<{
        label: String;
        hoursAndMinutes: {
            hours: number;
            minutes: number;
        };
    }> = [];

    constructor(
        private serviceComm: ServiceComm,
        private serviceTunnelFlight: TunnelFlightService,
        private translateService: TranslateService,
        private dateService: DateService,
    ) {}

    ngOnInit() {
        this.serviceComm.forceTranslateTitle.subscribe((data) => {
            if (data === true) {
                this.updateTitle();
            }
        });
        this.updateTitle();

        this.chartConfig();
        this.selectedPeriod = "currentYear";
        this.getDataForGraph();
    }

    public onPeriodChange() {
        this.getDataForGraph();
        if (this.dataSourceTable?.data.length > 0) {
            this.getDataForTable();
        }
    }

    public onLoadTable() {
        this.getDataForTable();
    }

    public delete(item: TunnelFlight) {
        let data: Array<TunnelFlight> = this.dataSourceTable.data;
        data = data.filter((d) => d.id !== item.id);

        this.dataSourceTable.data = data;
        this.serviceTunnelFlight.deleteTunnelFlight(item);
    }

    private chartConfig() {
        this.barChartType = "bar";
        this.barChartOptions = {
            responsive: true,
            maintainAspectRatio: true,
            plugins: {
                legend: {
                    display: true,
                },
                tooltip: {
                    callbacks: {
                        footer: this.footer,
                    },
                },
            },
            interaction: {
                intersect: false,
                mode: "nearest",
                axis: "x",
            },
            scales: {
                x: {
                    stacked: true,
                },
                y: {
                    stacked: true,
                },
            },
        };
    }

    private updateTitle() {
        this.translateService
            .get("ListTunnelFlight_Title")
            .subscribe((data) => {
                this.serviceComm.updatedComponentTitle(data);
            });
    }

    private getDataForTable(): void {
        this.isLoading = true;

        // Get data to show in a table
        let endDate = new Date();
        endDate.setHours(0, 0, 0, 0);
        let beginDate = this.computeBeginDateByPeriod(
            this.selectedPeriod,
            endDate,
        );

        this.serviceTunnelFlight
            .getTunnelFlights(beginDate, endDate)
            .subscribe((data) => {
                this.dataSourceTable.data = data;
                this.isLoading = false;
            });
    }

    private getDataForGraph(): void {
        this.isLoading = true;

        // Get data to show in a table
        let endDate = new Date();
        endDate.setHours(0, 0, 0, 0);
        let beginDate = this.computeBeginDateByPeriod(
            this.selectedPeriod,
            endDate,
        );

        this.serviceTunnelFlight
            .getTunnelFlightsByMonth(beginDate, endDate)
            .subscribe((data) => {
                const allMonths = this.getMontsBetweenDates(beginDate, endDate);
                const cumulatedTime = this.getCumulatedTimeByTypeByMonth(
                    data,
                    allMonths,
                );
                this.computeTimeByType(data);
                this.barChartData = {
                    labels: allMonths,
                    datasets: cumulatedTime,
                };

                this.isLoading = false;
            });
    }

    private computeTimeByType(stats: TunnelFlightByMonth[]) {
        this.stats = [];
        from(stats)
            .pipe(
                groupBy((type) => type.type, { element: (p) => p.nb }),
                mergeMap((group$) =>
                    group$.pipe(
                        reduce((acc, cur) => [...acc, cur], [`${group$.key}`]),
                    ),
                ),
                map((arr) => ({
                    label: arr[0],
                    values: arr
                        .slice(1)
                        .reduce((a, b) => Number(a) + Number(b), 0),
                    hoursAndMinutes: {},
                })),
            )
            .subscribe((p) => {
                let newStat: {
                    label: String;
                    hoursAndMinutes: {
                        hours: number;
                        minutes: number;
                    };
                } = {
                    label: p.label.toString(),
                    hoursAndMinutes: this.toHoursAndMinutes(+p.values),
                };
                this.stats.push(newStat);
            });
    }

    private getMontsBetweenDates(
        beginDate: Date,
        endDate: Date,
    ): Array<string> {
        let results: Array<string> = [];
        let tmpBeginDate = new Date(beginDate.getTime());
        const tmpEndDate = new Date(endDate.getTime());

        while (tmpBeginDate < tmpEndDate) {
            results.push(formatDate(tmpBeginDate, "yy-MM", "en"));
            tmpBeginDate.setMonth(tmpBeginDate.getMonth() + 1);
        }

        return results;
    }

    private getCumulatedTimeByTypeByMonth(
        stats: TunnelFlightByMonth[],
        allMonths: string[],
    ): Array<any> {
        let tmpResults = new Map<string, number[]>();

        const disctintType = Array.from(
            new Set(stats.map((item) => item.type)),
        );
        disctintType.forEach((type) => {
            tmpResults.set(
                type,
                Array.from({ length: allMonths.length }, (v, k) => 0),
            );
        });

        for (let i = 0; i < allMonths.length; i++) {
            const month = allMonths[i];
            let filteredStats = stats.filter((d) => d.month == month);

            if (filteredStats.length > 0) {
                filteredStats.forEach((fs) => {
                    tmpResults.get(fs.type)[i] += fs.nb;
                });
            }
        }

        const results = Array.from(tmpResults, function (item) {
            return { label: item[0], data: item[1] };
        });

        return results;
    }

    private footer = (tooltipItems: any) => {
        let sum = 0;

        tooltipItems.forEach(function (tooltipItem: any) {
            sum += tooltipItem.parsed.y;
        });

        return "Sum: " + sum;
    };

    private computeBeginDateByPeriod(
        selectedPeriod: String,
        endDate: Date,
    ): Date {
        let beginDate = new Date();

        switch (selectedPeriod) {
            case "currentYear":
                beginDate = new Date(endDate.getFullYear(), 0, 1);
                break;
            case "12Months":
                beginDate = this.dateService.addMonths(endDate, -12);
                beginDate.setDate(1);
                break;
            case "all":
                beginDate = this.dateService.addMonths(endDate, -120);
                beginDate.setDate(1);
                break;
        }

        return beginDate;
    }

    private toHoursAndMinutes(totalMinutes: number): {
        hours: number;
        minutes: number;
    } {
        const hours = Math.floor(totalMinutes / 60);
        const minutes = totalMinutes % 60;

        return { hours, minutes };
    }
}
