import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, ElementRef, ViewEncapsulation, HostListener, inject } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { tap, catchError, switchMap, filter, shareReplay, retry, map } from 'rxjs/operators';
import { BehaviorSubject, combineLatest, of, Subject } from 'rxjs';
import { State } from '@progress/kendo-data-query';
import { MessageService } from '../_shared/services/message.service';
import { NotifyService } from '../_shared/services/notify.service';
import { saveAs } from '@progress/kendo-file-saver';
import * as util from '../_shared/utils/util';
import { TemplateService, TemplateListItem, TemplateDetail } from './template.service';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../app.config';
import { FileRestrictions, SelectEvent } from '@progress/kendo-angular-upload';

@Component({
    selector: 'app-template',
    templateUrl: './template.component.html',
    encapsulation: ViewEncapsulation.None,
    changeDetection: ChangeDetectionStrategy.OnPush,
    imports: [FAST_PAGE_COMMON, FAST_KENDO_COMMON]
})
export class TemplateComponent {
    private service = inject(TemplateService);
    private messageService = inject(MessageService);
    private titleService = inject(Title);
    private ref = inject(ChangeDetectorRef);
    private notify = inject(NotifyService);

    @ViewChild("grid", { read: ElementRef }) kendoGridEl: ElementRef;
    @HostListener('window:resize') onResize() { };

    util = util;
    gridScrollPosition: util.GridScrollPosition = { topPos: 0, leftPos: 0 };
    hasModifyPermission = false;
    mySelection: number[] = [];
    selectedTemplate: TemplateDetail | null = null;

    gridLoading$ = new BehaviorSubject<boolean>(true);
    refreshItems$ = new BehaviorSubject<string | null>(null);
    exporting$ = new BehaviorSubject<boolean>(false);
    detailOpened$ = new BehaviorSubject<boolean>(false);
    detailLoading$ = new BehaviorSubject<boolean>(false);
    refreshDetail$ = new BehaviorSubject<number | null>(null);
    uploadSuccess$ = new BehaviorSubject<boolean>(false);
    refreshRequiredData$ = new BehaviorSubject(null);
    exportClicked$ = new Subject();

    state: State = {
        filter: null,
        group: null,
        skip: 0,
        sort: [{ field: 'Directory', dir: 'asc' }, { field: 'Name', dir: 'asc' }],
        take: 50
    };

    templateRestrictions: FileRestrictions = {
        allowedExtensions: ['docx']
    };

    requiredData$ = this.refreshRequiredData$.pipe(
        switchMap(refreshType => {
            return combineLatest([this.service.requiredData$, of(refreshType)]);
        }),
        map(([requiredData]) => {
            this.hasModifyPermission = requiredData.hasModifyPermission;
            return requiredData;
        }),
        shareReplay(1),
        catchError(err => {
            return util.handleError(err, this.messageService)
        }), retry(10)
    )

    items$ = this.refreshItems$.pipe(
        tap(() => {
            this.gridLoading$.next(true);
        }),
        switchMap(() => {
            return this.service.getItems(this.state);
        }),
        tap(() => {
            this.gridLoading$.next(false);
            util.goToSavedGridScrollPos(this.kendoGridEl, this.gridScrollPosition);
        }),
        shareReplay(1),
        catchError(err => {
            return util.handleError(err, this.messageService);
        }), retry(10)
    );

    exportAction$ = this.exportClicked$.pipe(
        tap(() => {
            this.exporting$.next(true);
        }),
        switchMap(() => {
            return this.service.exportItems(this.state, 'Templates.xlsx');
        }),
        tap(res => {
            saveAs(res.fileBlob, res.fileName);
            this.exporting$.next(false);
        }),
        shareReplay(1),
        catchError(err => {
            this.exporting$.next(false);
            return util.handleError(err, this.messageService);
        }), retry(10)
    )

    detail$ = this.refreshDetail$.pipe(
        filter(id => id !== null),
        switchMap(id => {
            this.detailLoading$.next(true);
            return this.service.getDetail(id!);
        }),
        tap((template) => {
            this.selectedTemplate = template;
            this.detailLoading$.next(false);
            this.uploadSuccess$.next(false);
        }),
        shareReplay(1),
        catchError(err => {
            this.closeDetail();
            return util.handleError(err, this.messageService);
        }), retry(10)
    );

    openDetail(dataItem: TemplateListItem): void {
        util.saveGridScrollPos(this.kendoGridEl, this.gridScrollPosition);
        this.refreshDetail$.next(dataItem.Id);
        this.detailOpened$.next(true);
    }

    closeDetail = () => {
        this.detailOpened$.next(false);
        this.selectedTemplate = null;
        this.uploadSuccess$.next(false);
    }

    downloadTemplate(): void {
        if (this.selectedTemplate) {
            this.service.downloadTemplate(this.selectedTemplate.id);
        }
    }

    getUploadUrl(): string {
        return this.selectedTemplate ? this.service.getUploadUrl(this.selectedTemplate.id) : '';
    }

    onUploadSuccess(): void {
        this.uploadSuccess$.next(true);
        this.notify.success('Template uploaded successfully');
    }

    onUploadError(): void {
        this.notify.error('Upload failed. Please try again.');
    }

    onUploadSelect(value: SelectEvent): void {
        const extension: string = value.files[0].extension.replace('.', '');
        if (!extension || extension.length === 0 || !this.templateRestrictions.allowedExtensions.includes(extension)) {
            this.notify.error("Invalid file type");
        }
    }

    export(): void {
        this.exportClicked$.next(null);
    }

    dataStateChange(state: State): void {
        this.gridScrollPosition.topPos = 0;
        this.gridScrollPosition.leftPos = 0;
        util.fixKendoQueryFilter(state.filter);
        this.state = state;
        this.refreshItems$.next(null);
    }

    title$ = of('Template').pipe(
        tap((title) => util.trySetTitle(this.titleService, title))
    );
}

