import { CommonModule } from '@angular/common';
import { trigger, state, style, animate, transition } from '@angular/animations';
import { Component, AfterContentChecked, OnInit, OnDestroy, computed, signal, Signal, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatSliderModule } from '@angular/material/slider';
import {
    MatSnackBar,
    MatSnackBarConfig,
    MatSnackBarHorizontalPosition,
    MatSnackBarModule,
    MatSnackBarVerticalPosition,
} from '@angular/material/snack-bar';
import { Observable, Subscription } from 'rxjs';
import { take } from 'rxjs/operators';

import { JumpAddDialogComponent } from '@components/logbook/dialogs';
import { MenuItems, JumpTableComponent } from '@components/shared';
import { Errors, Jump, JumpFile, JumpFileType, JumpListConfig, Range, User, X2Data } from '@models';
import { JumpsService, JumpTableService, UserService } from '@services';

@Component({
    imports: [
        CommonModule,
        RouterModule,
        FormsModule,
        MatButtonModule,
        MatCardModule,
        MatDividerModule,
        MatExpansionModule,
        MatIconModule,
        MatInputModule,
        MatMenuModule,
        MatDialogModule,
        MatSliderModule,
        MatSnackBarModule,
        JumpTableComponent,
    ],
    selector: 'app-logbook',
    templateUrl: './logbook.component.html',
    styleUrl: './logbook.component.scss',
    animations: [
        trigger('flyInOut', [
            state('in', style({ transform: 'translateY(0)' })),
            transition('void => *', [style({ transform: 'translateY(-100%)' }), animate(200)]),
            transition('* => void', [style({ transform: 'translateY(100%)' }), animate(200)]),
        ]),
    ],
})
export class LogbookComponent implements OnInit, OnDestroy, AfterContentChecked {
    private route = inject(ActivatedRoute);
    private router = inject(Router);
    private dialog = inject(MatDialog);
    private snackBar = inject(MatSnackBar);
    private _jumpsService = inject(JumpsService);
    private _jumpTableService = inject(JumpTableService);
    private _userService = inject(UserService);
    menuItems = inject(MenuItems);

    private _currentUser: Subscription = new Subscription();
    private _subscriptions: Array<Subscription> = [];
    private _data: Subscription = new Subscription();
    private _lastjump: Subscription = new Subscription();
    private _lastjump$: Observable<Jump> = new Observable();
    public jumpsCount = 0;
    public errors!: Errors;
    public title = 'Carnet de sauts';
    public jump: Jump = {} as Jump;
    public lastJump: Jump = {} as Jump;
    public currentUser: User = {} as User;
    public listConfig: JumpListConfig = { type: 'all', filters: {} };
    public isAuthenticated = false;
    public isUser = true;
    public canModify = false;
    public isSubmitting = false;
    public tableRefresh = false;
    public jumpRefresh = false;
    public jumpsSinceHundred: Signal<number> = signal(0);
    public jumpsToHundred: Signal<number> = signal(100);
    public rangesMinMax: {
        numero: Range;
        taille: Range;
        participants: Range;
        hauteur: Range;
        year: Range;
    } = {
        numero: { start: 1, end: 100000 },
        taille: { start: 1, end: 400 },
        participants: { start: 1, end: 200 },
        hauteur: { start: 0, end: 10000 },
        year: { start: 1970, end: 2020 },
    };

    ngOnInit() {
        //this.importJumps();
        const data$: Observable<{ isAuthenticated: boolean; lastjump: Jump }> = this.route.data as Observable<{
            isAuthenticated: boolean;
            lastjump: Jump;
        }>;
        this._data = data$.subscribe((data: { isAuthenticated: boolean; lastjump: Jump }) => {
            this.isAuthenticated = data.isAuthenticated;
            if (!this.isAuthenticated) {
                this.router.navigateByUrl('/login');
                return;
            }

            const currentUser$: Observable<User> = this._userService.currentUser;
            this._currentUser = currentUser$.subscribe((userData: User) => {
                this.currentUser = userData;
                this.canModify = this.currentUser.role === 'Admin';
            });

            const currentYear: number = new Date().getFullYear();
            this.lastJump = data['lastjump'];
            this.rangesMinMax = {
                numero: { start: 1, end: data.lastjump.numero },
                taille: { start: data.lastjump.taille!, end: 230 },
                participants: { start: 1, end: 20 },
                hauteur: { start: 1000, end: 8000 },
                year: { start: 2018, end: currentYear },
            };
            this.listConfig.filters.numeroRangeStart = this.rangesMinMax.numero.start;
            this.listConfig.filters.numeroRangeEnd = this.rangesMinMax.numero.end;
            this.listConfig.filters.tailleRangeStart = this.rangesMinMax.taille.start;
            this.listConfig.filters.tailleRangeEnd = this.rangesMinMax.taille.end;
            this.listConfig.filters.participantsRangeStart = this.rangesMinMax.participants.start;
            this.listConfig.filters.participantsRangeEnd = this.rangesMinMax.participants.end / 2;
            this.listConfig.filters.hauteurRangeStart = this.rangesMinMax.hauteur.start;
            this.listConfig.filters.hauteurRangeEnd = this.rangesMinMax.hauteur.end;
            this.listConfig.filters.yearRangeStart = this.rangesMinMax.year.start;
            this.listConfig.filters.yearRangeEnd = this.rangesMinMax.year.end;
            this.jumpsSinceHundred = computed(() => this.lastJump.numero % 100);
            this.jumpsToHundred = computed(() => 100 - this.jumpsSinceHundred());
            /*
            this._lastjump$ = this._jumpsService.getLastJump();
            this._lastjump = this._lastjump$.subscribe((jump) => {
                const currentYear: number = new Date().getFullYear();
                this.lastJump = jump;
                this.rangesMinMax = {
                    numero: {start: 1, end: jump.numero},
                    taille: {start: jump.taille!, end: 230},
                    participants: {start: 1, end: 20},
                    hauteur: {start: 1000, end: 8000},
                    year: {start: 2018, end: currentYear}
                }
                this.listConfig.filters.numeroRangeStart = this.rangesMinMax.numero.start;
                this.listConfig.filters.numeroRangeEnd = this.rangesMinMax.numero.end;
                this.listConfig.filters.tailleRangeStart = this.rangesMinMax.taille.start;
                this.listConfig.filters.tailleRangeEnd = this.rangesMinMax.taille.end;
                this.listConfig.filters.participantsRangeStart = this.rangesMinMax.participants.start;
                this.listConfig.filters.participantsRangeEnd = (this.rangesMinMax.participants.end / 2);
                this.listConfig.filters.hauteurRangeStart = this.rangesMinMax.hauteur.start;
                this.listConfig.filters.hauteurRangeEnd = this.rangesMinMax.hauteur.end;
                this.listConfig.filters.yearRangeStart = this.rangesMinMax.year.start;
                this.listConfig.filters.yearRangeEnd = this.rangesMinMax.year.end;
                this.jumpsSinceHundred = computed(() => (this.lastJump.numero % 100));
                this.jumpsToHundred = computed(() => (100 - this.jumpsSinceHundred()));
            });
            */
        });
    }

    ngOnDestroy() {
        this._data.unsubscribe();
        this._currentUser.unsubscribe();
        this._lastjump.unsubscribe();
        this._subscriptions.forEach((subscription) => {
            subscription.unsubscribe();
        });
    }

    ngAfterContentChecked() {
        if (this.tableRefresh != this._jumpTableService.tableRefresh) {
            this.tableRefresh = this._jumpTableService.tableRefresh;
            this._lastjump$ = this._jumpsService.getLastJump();
            this._lastjump = this._lastjump$.subscribe((jump) => {
                this.lastJump = jump;
            });
            console.log('lastjump has been updated in logbook component');
        }
    }

    openAddDialog(): void {
        this.jump = <Jump>{};
        this.jump.numero = this.lastJump.numero + 1;
        this.jump.sautants = [];
        this.jump.participants = 1;
        const dialogRef = this.dialog.open(JumpAddDialogComponent, {
            width: '70vw',
            data: { jump: this.jump, lastJump: this.lastJump },
        });
        dialogRef
            .afterClosed()
            .pipe(take(1))
            .subscribe((result) => {
                if (result != undefined) {
                    this._onEntry(result);
                }
            });
    }

    setTotalResults(value: number): void {
        this.jumpsCount = value;
    }

    private _loadSkydiverIdJumps(jumps: Array<Jump>) {
        const config: JumpListConfig = { type: 'all', filters: {} } as JumpListConfig;
        config.filters.limit = 200;
        config.filters.offset = 0;
        config.filters.dateRangeStart = `${jumps[0].date!.substring(0, 10)} 00:00:00`;
        config.filters.dateRangeEnd = `${jumps[0].date!.substring(0, 10)} 23:59:59`;
        const subscription: Subscription = this._jumpsService
            .getAllFromSkydiverIdApi(config)
            .pipe(take(1))
            .subscribe({
                next: (data) => {
                    if (data.items.length) {
                        const diff = jumps.length - data.items.length;
                        if (diff < 0) {
                            console.error(
                                `${diff} ${diff > 1 ? 'données' : 'donnée'} altimètre de trop le ${jumps[0].date!.substring(0, 10)}`,
                            );
                        }
                        if (diff > 0) {
                            console.error(
                                `${diff}/${jumps.length} ${diff > 1 ? 'données altimètre manquantes' : 'donnée altimètre manquante'} le ${jumps[0].date!.substring(0, 10)}`,
                                jumps,
                                data.items,
                            );
                        }
                        console.log(
                            `${jumps.length} ${jumps.length > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`,
                            jumps.sort((a, b) => b.numero! - a.numero!),
                            data.items,
                        );
                        this._addX2Data(data.items, jumps);
                    } else {
                        console.info('Aucune données altimètre pour le ', jumps[0].date!.substring(0, 10));
                    }
                },
                error: (err) => {
                    console.error('getAllFromSkydiverIdApi error');
                    this.errors = err;
                },
            });
        this._subscriptions.push(subscription);
    }

    private _addJump(jumps: Array<Jump>, index: number = 0) {
        this._jumpsService
            .save(jumps[index])
            .pipe(take(1))
            .subscribe({
                next: (jump) => {
                    jumps[index] = jump;
                    console.log('Ajout du saut', jump.numero);
                    Object.assign(this.jump, jump);
                    this._openSnackBar(`Le saut n°${jump.numero} a été ajouté !`, 'Annuler');
                    this._resetErrors();
                    if (jumps.length == index + 1) {
                        this._loadSkydiverIdJumps(jumps);
                    } else {
                        this._addJump(jumps, index + 1);
                    }
                },
                error: (err) => {
                    this.errors = err;
                    this.isSubmitting = false;
                },
            });
    }

    private _addX2Data(x2data: Array<X2Data>, jumps: Array<Jump>, index: number = 0) {
        const file: JumpFile = {
            name: x2data[index].name,
            path: `/Volumes/Storage/Skydive/X2_Logs/${x2data[index].date!.substring(0, 4)}/`,
            type: JumpFileType.CSV,
        } as JumpFile;
        this._subscriptions.push(
            this._jumpsService
                .saveX2Data(jumps[index].slug!, file, x2data[index])
                .pipe(take(1))
                .subscribe({
                    next: (jump) => {
                        jumps[index] = jump;
                        console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
                        if (jumps.length == index + 1 || x2data.length == index + 1) {
                            this._addKmlFile(jumps);
                        } else {
                            this._addX2Data(x2data, jumps, index + 1);
                        }
                    },
                    error: (err) => {
                        this.errors = err;
                    },
                }),
        );
    }

    private _addKmlFile(jumps: Array<Jump>, index: number = 0) {
        if (jumps[index].x2data !== null) {
            //console.log(`Le fichier ${jump.x2data.name} existe pour le saut n°${jump.numero} !`, `https://skydiver.id/api/jump/${jump.x2data.id}/kml`);
            const file: JumpFile = {
                name: jumps[index].x2data.name.substring(0, -4) + '.kml',
                path: `/Volumes/Storage/Skydive/X2_Kmls/${jumps[index].date.substring(0, 4)}/`,
                type: JumpFileType.KML,
            } as JumpFile;
            this._subscriptions.push(
                this._jumpsService
                    .saveKml(jumps[index].slug, file)
                    .pipe(take(1))
                    .subscribe({
                        next: (file) => {
                            console.log(
                                `Le fichier ${file.path}${file.name} a été ajouté au saut n°${jumps[index].numero} !`,
                                'OK',
                            );
                            if (jumps.length == index + 1) {
                                this._resetErrors();
                                this.jumpRefresh = !this.jumpRefresh;
                                this._jumpTableService.updateJumpRefresh(this.jumpRefresh);
                                this._lastjump$ = this._jumpsService.getLastJump();
                                this._lastjump = this._lastjump$.subscribe((jump) => {
                                    this.lastJump = jump;
                                });
                            } else {
                                this._addKmlFile(jumps, index + 1);
                            }
                        },
                        error: (err) => {
                            this.errors = err;
                        },
                    }),
            );
        }
    }

    private _onEntry(jumps: Array<Jump>): void {
        if (jumps.length) {
            this._addJump(jumps);
        }
    }

    /*
    private _onEntry(jumps: Array<Jump>): void {
        const max: number = jumps.length;
        let refresh: boolean = false;
        jumps.forEach((jump: Jump, index: number) => {
            console.log('Ajout du saut', jump.numero);
            Object.assign(this.jump, jump);
            if (max == index+1) {
                refresh = true;
            }
            this._saveJump(jump, refresh);
        });
    }

    private _saveJump(jump: Jump, refresh: boolean) {
        this._jumpsService.save(jump)
        .pipe(take(1))
        .subscribe({
            next: (jump) => {
                this._openSnackBar(`Le saut n°${jump.numero} a été ajouté !`, 'Annuler');
                this._resetErrors();
                if (refresh) {
                    this.jumpRefresh = !this.jumpRefresh;
                    this._jumpTableService.updateJumpRefresh(this.jumpRefresh);
                    this._lastjump$ = this._jumpsService.getLastJump();
                    this._lastjump = this._lastjump$.subscribe((jump) => {
                        this.lastJump = jump;
                    });
                }
            },
            error: (err) => {
                this.errors = err;
                this.isSubmitting = false;
            }
        });
    }
    */

    private _openSnackBar(content: string, title: string) {
        const config: MatSnackBarConfig = {
            horizontalPosition: <MatSnackBarHorizontalPosition>'end',
            verticalPosition: <MatSnackBarVerticalPosition>'bottom',
            duration: 4000,
        };
        this.snackBar.open(content, title, config);
    }

    private _resetErrors(): void {
        this.errors = { errors: {} };
    }
}
