import { Injectable, Injector } from '@angular/core';
import { List } from '@iqser/common-ui/lib/utils';
import { ActivatedRouteSnapshot, IsActiveMatchOptions, NavigationEnd, Router } from '@angular/router';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { filter, pluck } from 'rxjs/operators';
import { FilesMapService } from './files/files-map.service';
import { TranslateService } from '@ngx-translate/core';
import { BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, FILE_ID } from '@red/domain';
import { DossiersService } from './dossiers/dossiers.service';
import { dossiersServiceResolver } from './entity-services/dossiers.service.provider';
import { FeaturesService } from './features.service';
import { DashboardStatsService } from './dossier-templates/dashboard-stats.service';

export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions;
export type BreadcrumbDisplayType = 'text' | 'dropdown';

export interface Breadcrumb {
    readonly name$: Observable<string>;
    readonly type: BreadcrumbDisplayType;
    readonly options: {
        readonly routerLink?: string[];
        readonly routerLinkActiveOptions?: RouterLinkActiveOptions | undefined;
        readonly clamp?: boolean;
        readonly options?: Breadcrumb[];
        readonly activeOption?: Breadcrumb;
    };
}

export type Breadcrumbs = List<Breadcrumb>;

@Injectable({
    providedIn: 'root',
})
export class BreadcrumbsService {
    readonly #store$ = new BehaviorSubject<Breadcrumbs>([]);
    readonly breadcrumbs$: Observable<Breadcrumbs>;

    constructor(
        private readonly _injector: Injector,
        private readonly _router: Router,
        private readonly _translateService: TranslateService,
        private readonly _filesMapService: FilesMapService,
        private readonly _dashboardStatsService: DashboardStatsService,
        private readonly _featuresService: FeaturesService,
    ) {
        this.breadcrumbs$ = this.#store$.asObservable();

        _router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
            const root = _router.routerState.snapshot.root;
            this._clear();
            this._addBreadcrumbs(root);
        });
    }

    get breadcrumbs() {
        return this.#store$.value;
    }

    private get _dossiersService(): DossiersService {
        return dossiersServiceResolver(this._injector, this._router);
    }

    private get _dashboardBreadcrumb(): Breadcrumb {
        return {
            name$: of(this._translateService.instant('top-bar.navigation-items.dashboard')),
            type: 'text' as BreadcrumbDisplayType,
            options: {
                routerLink: ['/main', 'dashboard'],
                routerLinkActiveOptions: { exact: true },
            },
        };
    }

    private _append(breadcrumb: Breadcrumb) {
        this.#store$.next([...this.#store$.value, breadcrumb]);
    }

    private _clear() {
        this.#store$.next([]);
    }

    private _addBreadcrumbs(route: ActivatedRouteSnapshot, params: Record<string, string> = {}) {
        if (route.firstChild) {
            this._addBreadcrumbs(route.firstChild, { ...params, ...route.params });
            return;
        }

        const breadcrumbs = route.data.breadcrumbs || [];

        if (breadcrumbs.length === 1 && this._featuresService.isEnabled(DOSSIERS_ARCHIVE)) {
            if (breadcrumbs[0] === BreadcrumbTypes.dossierTemplate) {
                this._addDossierTemplateDropdown(params);
                return;
            }
            if (breadcrumbs[0] === BreadcrumbTypes.dashboard) {
                this._append(this._dashboardBreadcrumb);
                return;
            }
        }

        for (const breadcrumb of breadcrumbs) {
            switch (breadcrumb) {
                case BreadcrumbTypes.dossierTemplate:
                    this._append(this._dossierTemplateBreadcrumb(params));
                    break;
                case BreadcrumbTypes.dossier:
                    this._addDossierBreadcrumb(params);
                    break;
                case BreadcrumbTypes.file:
                    this._addFileBreadcrumb(params);
                    break;
            }
        }
    }

    private _addDossierTemplateDropdown(params: Record<string, string>): void {
        const breadcrumbs = [this._dashboardBreadcrumb].concat(
            this._dashboardStatsService.all
                .filter(dt => !dt.isEmpty)
                .map(dt => this._dossierTemplateBreadcrumb({ dossierTemplateId: dt.id })),
        );

        const activeOption = breadcrumbs.find(b => b.options.routerLink[1] === params[DOSSIER_TEMPLATE_ID]);

        this._append({
            name$: activeOption.name$,
            type: 'dropdown' as BreadcrumbDisplayType,
            options: {
                options: breadcrumbs,
                activeOption,
            },
        });
    }

    private _dossierTemplateBreadcrumb(params: Record<string, string>): Breadcrumb {
        const dossierTemplateId = params[DOSSIER_TEMPLATE_ID];
        return {
            name$: this._dashboardStatsService.getEntityChanged$(dossierTemplateId).pipe(pluck('name')),
            type: 'text' as BreadcrumbDisplayType,
            options: {
                routerLink: ['/main', dossierTemplateId, this._dossiersService.routerPath],
                routerLinkActiveOptions: { exact: true },
                clamp: true,
            },
        };
    }

    private _addDossierBreadcrumb(params: Record<string, string>): void {
        const dossiersService = this._dossiersService;
        const dossierId: string = params[DOSSIER_ID];
        this._append({
            name$: dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')),
            type: 'text' as BreadcrumbDisplayType,
            options: {
                routerLink: [dossiersService.find(dossierId).routerLink],
                routerLinkActiveOptions: { exact: true },
                clamp: true,
            },
        });
    }

    private _addFileBreadcrumb(params: Record<string, string>): void {
        const dossierId: string = params[DOSSIER_ID];
        const fileId: string = params[FILE_ID];
        this._append({
            name$: this._filesMapService.watch$(dossierId, fileId).pipe(pluck('filename')),
            type: 'text' as BreadcrumbDisplayType,
            options: {
                routerLink: [this._filesMapService.get(dossierId, fileId).routerLink],
                clamp: true,
            },
        });
    }
}
