import { GenericService, LAST_CHECKED_OFFSET, QueryParam, ROOT_CHANGES_KEY } from '@iqser/common-ui';
import { Dossier, DossierStats, IDossierChanges } from '@red/domain';
import { forkJoin, Observable, of, Subscription, throwError, timer } from 'rxjs';
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { NGXLogger } from 'ngx-logger';
import { ActiveDossiersService } from './active-dossiers.service';
import { ArchivedDossiersService } from './archived-dossiers.service';
import { inject, Injectable, OnDestroy } from '@angular/core';
import { DashboardStatsService } from '../dossier-templates/dashboard-stats.service';
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
import { List } from '@iqser/common-ui/lib/utils';

@Injectable({ providedIn: 'root' })
export class DossiersChangesService extends GenericService<Dossier> implements OnDestroy {
    readonly #subscription = new Subscription();
    readonly #activeDossiersService = inject(ActiveDossiersService);
    readonly #archivedDossiersService = inject(ArchivedDossiersService);
    readonly #dashboardStatsService = inject(DashboardStatsService);
    readonly #logger = inject(NGXLogger);
    protected readonly _defaultModelPath = 'dossier';

    loadOnlyChanged(): Observable<IDossierChanges> {
        const removeIfNotFound = (id: string) =>
            catchError((error: unknown) => {
                if (error instanceof HttpErrorResponse && error.status === HttpStatusCode.NotFound) {
                    this.#activeDossiersService.remove(id);
                    this.#archivedDossiersService.remove(id);
                    return of([]);
                }
                return throwError(() => error);
            });

        const load = (changes: IDossierChanges) =>
            changes.map(change => this.#load(change.dossierId).pipe(removeIfNotFound(change.dossierId)));

        return this.hasChangesDetails$().pipe(
            tap(changes => this.#logger.info('[DOSSIERS_CHANGES] Found changes', changes)),
            switchMap(dossierChanges =>
                forkJoin([...load(dossierChanges), this.#dashboardStatsService.loadAll().pipe(take(1))]).pipe(map(() => dossierChanges)),
            ),
        );
    }

    hasChangesDetails$(): Observable<IDossierChanges> {
        const body = { value: this._lastCheckedForChanges.get(ROOT_CHANGES_KEY) };
        const dateBeforeRequest = new Date(Date.now() - LAST_CHECKED_OFFSET).toISOString();

        this.#logger.info('[DOSSIERS_CHANGES] Check with Last Checked Date', body.value);

        return this._post<IDossierChanges>(body, `${this._defaultModelPath}/changes/details`).pipe(
            filter(changes => changes.length > 0),
            tap(() => this._lastCheckedForChanges.set(ROOT_CHANGES_KEY, dateBeforeRequest)),
            tap(() => this.#logger.info('[DOSSIERS_CHANGES] Save Last Checked Date value', dateBeforeRequest)),
        );
    }

    initialize() {
        this.#logger.info('[DOSSIERS_CHANGES] Initialize timer');
        const subscription = timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL).pipe(
            switchMap(() => this.loadOnlyChanged()),
            tap(changes => {
                this.#activeDossiersService.emitFileChanges(changes);
                this.#archivedDossiersService.emitFileChanges(changes);
            }),
        );
        this.#subscription.add(subscription.subscribe());
    }

    ngOnDestroy() {
        this.#subscription.unsubscribe();
    }

    #load(id: string): Observable<DossierStats[]> {
        const queryParams: List<QueryParam> = [{ key: 'includeArchived', value: true }];
        return super._getOne([id], this._defaultModelPath, queryParams).pipe(
            map(entity => new Dossier(entity)),
            switchMap((dossier: Dossier) => {
                if (dossier.isArchived) {
                    this.#activeDossiersService.remove(dossier.id);
                    return this.#archivedDossiersService.updateDossier(dossier);
                }
                this.#archivedDossiersService.remove(dossier.id);
                return this.#activeDossiersService.updateDossier(dossier);
            }),
        );
    }
}
