import * as deepMerge from 'deepmerge';
import {BehaviorSubject, Observable} from 'rxjs';

import {PodGroupType} from '../../applications/components/application-pod-view/pod-view';
import {UserMessages} from '../models';

export type AppsDetailsViewType = 'tree' | 'network' | 'list' | 'pods';

export enum AppsDetailsViewKey {
    Tree = 'tree',
    Network = 'network',
    List = 'list',
    Pods = 'pods'
}

export type AppSetsDetailsViewType = 'tree' | 'list';

export enum AppSetsDetailsViewKey {
    Tree = 'tree',
    List = 'list'
}

export interface AbstractAppDetailsPreferences {
    resourceFilter: string[];
    darkMode: boolean;
    hideFilters: boolean;
    groupNodes?: boolean;
    zoom: number;
    view: AppsDetailsViewType | AppSetsDetailsViewType | string;
    resourceView: 'manifest' | 'diff' | 'desiredManifest';
    inlineDiff: boolean;
    compactDiff: boolean;
    hideManagedFields?: boolean;
    orphanedResources: boolean;
}

export interface AppDetailsPreferences extends AbstractAppDetailsPreferences {
    view: AppsDetailsViewType | string;
    podView: PodViewPreferences;
    followLogs: boolean;
    matchCase: boolean;
    wrapLines: boolean;
    podGroupCount: number;
    userHelpTipMsgs: UserMessages[];
    enableWordWrap?: boolean;
}

export interface AppSetDetailsPreferences extends AbstractAppDetailsPreferences {
    view: AppSetsDetailsViewType | string;
}

export interface PodViewPreferences {
    sortMode: PodGroupType;
    hideUnschedulable: boolean;
    sortOrder?: 'asc' | 'desc';
}

export interface HealthStatusBarPreferences {
    showHealthStatusBar: boolean;
}

export type AppsListViewType = 'tiles' | 'list' | 'summary';

export enum AppsListViewKey {
    List = 'list',
    Summary = 'summary',
    Tiles = 'tiles'
}

export class AbstractAppsListPreferences {
    public static clearFilters(pref: AbstractAppsListPreferences) {
        pref.healthFilter = [];
        pref.labelsFilter = [];
        pref.showFavorites = false;
    }

    public labelsFilter: string[];
    public healthFilter: string[];
    public view: AppsListViewType;
    public hideFilters: boolean;
    public statusBarView: HealthStatusBarPreferences;
    public showFavorites: boolean;
    public favoritesAppList: string[];
}

export class AppsListPreferences extends AbstractAppsListPreferences {
    public static countEnabledFilters(pref: AppsListPreferences) {
        return [pref.clustersFilter, pref.healthFilter, pref.labelsFilter, pref.namespacesFilter, pref.projectsFilter, pref.reposFilter, pref.syncFilter].reduce(
            (count, filter) => {
                if (filter && filter.length > 0) {
                    return count + 1;
                }
                return count;
            },
            0
        );
    }

    public static clearFilters(pref: AppsListPreferences) {
        super.clearFilters(pref);

        pref.clustersFilter = [];
        pref.namespacesFilter = [];
        pref.projectsFilter = [];
        pref.reposFilter = [];
        pref.syncFilter = [];
        pref.autoSyncFilter = [];
    }

    public projectsFilter: string[];
    public reposFilter: string[];
    public syncFilter: string[];
    public autoSyncFilter: string[];
    public namespacesFilter: string[];
    public clustersFilter: string[];
}

export class AppSetsListPreferences extends AbstractAppsListPreferences {
    public static countEnabledFilters(pref: AppSetsListPreferences) {
        return [pref.healthFilter, pref.labelsFilter].reduce((count, filter) => {
            if (filter && filter.length > 0) {
                return count + 1;
            }
            return count;
        }, 0);
    }

    public static clearFilters(pref: AppSetsListPreferences) {
        super.clearFilters(pref);
    }
}

export interface ViewPreferences {
    version: number;
    appDetails: AppDetailsPreferences;
    appList: AppsListPreferences;
    pageSizes: {[key: string]: number};
    sortOptions?: {[key: string]: string};
    hideBannerContent: string;
    hideSidebar: boolean;
    position: string;
    theme: string;
}

const VIEW_PREFERENCES_KEY = 'view_preferences';

const minVer = 5;

const DEFAULT_PREFERENCES: ViewPreferences = {
    version: 1,
    appDetails: {
        view: 'tree',
        hideFilters: false,
        resourceFilter: [],
        inlineDiff: false,
        compactDiff: false,
        hideManagedFields: true,
        resourceView: 'manifest',
        orphanedResources: false,
        podView: {
            sortMode: 'node',
            hideUnschedulable: true
        },
        darkMode: false,
        followLogs: false,
        matchCase: false,
        wrapLines: false,
        zoom: 1.0,
        podGroupCount: 15.0,
        userHelpTipMsgs: []
    },
    appList: {
        view: 'tiles' as AppsListViewType,
        labelsFilter: new Array<string>(),
        projectsFilter: new Array<string>(),
        namespacesFilter: new Array<string>(),
        clustersFilter: new Array<string>(),
        reposFilter: new Array<string>(),
        syncFilter: new Array<string>(),
        autoSyncFilter: new Array<string>(),
        healthFilter: new Array<string>(),
        hideFilters: false,
        showFavorites: false,
        favoritesAppList: new Array<string>(),
        statusBarView: {
            showHealthStatusBar: true
        }
    },
    pageSizes: {},
    hideBannerContent: '',
    hideSidebar: false,
    position: '',
    theme: 'light'
};

export class ViewPreferencesService {
    private preferencesSubj: BehaviorSubject<ViewPreferences>;

    public init() {
        if (!this.preferencesSubj) {
            this.preferencesSubj = new BehaviorSubject(this.loadPreferences());
            window.addEventListener('storage', () => {
                this.preferencesSubj.next(this.loadPreferences());
            });
        }
    }

    public getPreferences(): Observable<ViewPreferences> {
        return this.preferencesSubj;
    }

    public updatePreferences(change: Partial<ViewPreferences>) {
        const nextPref = Object.assign({}, this.preferencesSubj.getValue(), change, {version: minVer});
        window.localStorage.setItem(VIEW_PREFERENCES_KEY, JSON.stringify(nextPref));
        this.preferencesSubj.next(nextPref);
    }

    private loadPreferences(): ViewPreferences {
        let preferences: ViewPreferences;
        const preferencesStr = window.localStorage.getItem(VIEW_PREFERENCES_KEY);
        if (preferencesStr) {
            try {
                preferences = JSON.parse(preferencesStr);
            } catch (e) {
                preferences = DEFAULT_PREFERENCES;
            }
            if (!preferences.version || preferences.version < minVer) {
                preferences = DEFAULT_PREFERENCES;
            }
        } else {
            preferences = DEFAULT_PREFERENCES;
        }
        return deepMerge(DEFAULT_PREFERENCES, preferences);
    }
}
