import {Injectable} from "@angular/core";
import {
    ComponentLifecycleEventEmitter,
    TupleSelector,
    VortexStatusService
} from "@synerty/vortexjs";
import {PrivateDiagramTupleService} from "./PrivateDiagramTupleService";
import {GroupDispsTuple, ModelCoordSet, ModelSet} from "../tuples";
import {
    DispColor,
    DispLayer,
    DispLevel,
    DispLineStyle,
    DispTextStyle
} from "../../lookups";
import {BranchKeyToIdMapTuple} from "../branch/BranchKeyToIdMapTuple";
import {BranchService} from "@peek/peek_plugin_branch";


/** Diagram Lookups offline cacher
 *
 * This Service is never unloaded, it makes sure that the lookups that the diagram
 * needs are always stored in the local DB.
 *
 * For NS, This is where the embedded web version reads it from.
 *
 */
@Injectable()
export class PrivateDiagramOfflineCacherService extends ComponentLifecycleEventEmitter {

    private static readonly LookupTuples = [
        DispLevel,
        DispLayer,
        DispColor,
        DispTextStyle,
        DispLineStyle
    ];

    private lookupSubs = [];
    private dispGroupSubs = [];

    constructor(private tupleService: PrivateDiagramTupleService,
                vortexStatusService: VortexStatusService,
                private globalBranchService: BranchService) {
        super();

        // Delete data older than 7 days
        let date7DaysAgo = new Date(Date.now() - 7 * 24 * 3600 * 1000);

        let promise = null;
        if (vortexStatusService.snapshot.isOnline) {
            promise = this.tupleService.offlineStorage
                .deleteOldTuples(date7DaysAgo)
                .catch(err => console.log(`ERROR: Failed to delete old tuples`));

        } else {
            vortexStatusService.isOnline
                .takeUntil(this.onDestroyEvent)
                .filter((val) => val === true)
                .first()
                .subscribe(() => {
                    this.tupleService.offlineStorage
                        .deleteOldTuples(date7DaysAgo)
                        .catch(err => console.log(`ERROR: Failed to delete old tuples`));
                });
            promise = Promise.resolve();
        }

        promise
            .then(() => {
                this.loadModelSet();
                this.loadModelCoordSet();
                this.loadBranchToIdMap();
            });

    }

    /**
     * Cache Model Set
     *
     * This method caches the model set list for offline use.
     *
     */
    private loadModelSet() {

        let tupleSelector = new TupleSelector(ModelSet.tupleName, {});

        this.tupleService.offlineObserver
            .subscribeToTupleSelector(tupleSelector)
            .takeUntil(this.onDestroyEvent)
            .subscribe((modelSets: ModelSet[]) => {
                this.tupleService.offlineObserver.flushCache(tupleSelector);
                this.loadLookups(modelSets);

                for (let modelSet of modelSets) {

                    // HACK!!!
                    // force the global branch service to cache it's stuff
                    this.globalBranchService.branches(modelSet.key);
                }

            });
    }

    /**
     * Cache Model Set
     *
     * This method caches the coord sets
     *
     */
    private loadModelCoordSet() {

        let tupleSelector = new TupleSelector(ModelCoordSet.tupleName, {});

        this.tupleService.offlineObserver
            .subscribeToTupleSelector(tupleSelector)
            .takeUntil(this.onDestroyEvent)
            .subscribe((tuples: ModelCoordSet[]) => {
                this.tupleService.offlineObserver.flushCache(tupleSelector);
                this.loadDispGroups(tuples);
            });

    }

    /**
     * Cache Branch KeyToIdMap Tuple
     *
     * This method caches the coord sets
     *
     */
    private loadBranchToIdMap() {

        let tupleSelector = new TupleSelector(BranchKeyToIdMapTuple.tupleName, {});

        this.tupleService.offlineObserver
            .subscribeToTupleSelector(tupleSelector)
            .takeUntil(this.onDestroyEvent)
            .subscribe((tuples: BranchKeyToIdMapTuple[]) => {
                this.tupleService.offlineObserver.flushCache(tupleSelector);
            });

    }

    /**
     * Cache Lookups
     *
     * This method caches the lookups for a model set
     *
     */
    private loadLookups(modelSets: ModelSet[]) {

        while (this.lookupSubs.length)
            this.lookupSubs.pop().unsubscribe();

        for (let modelSet of modelSets) {
            for (let LookupTuple of PrivateDiagramOfflineCacherService.LookupTuples) {
                let tupleSelector = new TupleSelector(LookupTuple.tupleName, {
                    "modelSetKey": modelSet.key
                });

                let sub = this.tupleService.offlineObserver
                    .subscribeToTupleSelector(tupleSelector)
                    .takeUntil(this.onDestroyEvent)
                    .subscribe((tuples: any[]) => {
                        this.tupleService.offlineObserver.flushCache(tupleSelector);
                    });

                this.lookupSubs.push(sub);
            }
        }
    }

    /**
     * Load Disp Groups
     *
     * This method caches the DispGroups for coord sets.
     *
     */
    private loadDispGroups(coordSets: ModelCoordSet[]) {

        let subs = this.dispGroupSubs;

        while (subs.length)
            subs.pop().unsubscribe();

        for (let coordSet of coordSets) {
            if (coordSet.dispGroupTemplatesEnabled !== true)
                continue;

            let tupleSelector = new TupleSelector(GroupDispsTuple.tupleName, {
                "coordSetId": coordSet.id
            });

            let sub = this.tupleService.offlineObserver
                .subscribeToTupleSelector(tupleSelector)
                .takeUntil(this.onDestroyEvent)
                .subscribe((tuples: any[]) => {
                    this.tupleService.offlineObserver.flushCache(tupleSelector);
                });

            subs.push(sub);
        }

    }

}