import { ApplicationSettings } from '@nativescript/core';
import { Action, Selector, State, StateContext } from '@ngxs/store';
import { catchError, tap } from 'rxjs/operators';
import { APP_ID, wsOrganizationUnit } from '~/constants/api.constants';
import { USER_TOKEN, USER_UNIT_ID } from '~/constants/login-constants';
import { BaseOutput } from '~/core/entities/output/baseOutput';
import { WebApiService } from '~/core/services/web-api.service';
import { MeetingDateDelete } from '../entities/input/meetingDateDelete';
import { MeetingDateDetail } from '../entities/input/meetingDateDetail';
import { MeetingDateInsert } from '../entities/input/meetingDateInsert';
import { MeetingDateUpdate } from '../entities/input/meetingDateUpdate';
import { OccupationAllUnit } from '../entities/input/occupationAllUnit';
import { RealtyAll } from '../entities/input/realtyAll';
import { MeetingDateDetailResponse } from '../entities/output/meetingDateDetailResponse';
import { OccupationAllUnitResult } from '../entities/output/occupationAllUnitResult';
import { RealtyAllResult } from '../entities/output/realtyAllResult';
import {
    DeleteMeetingDate,
    InsertMeetingDate,
    LoadMeetingDateDetail,
    LoadOccupationAllUnit,
    LoadRealtyAll,
    UpdateMeetingDate
} from './meeting-date.actions';

export interface MeetingDateStateModel {
    loadingMeetingDateDetail: boolean;
    loadingOccupationAllUnit: boolean;
    loadingRealtyAll: boolean;
    loadingInsertMeetingDate: boolean;
    loadingDeleteMeetingDate: boolean;
    loadingUpdateMeetingDate: boolean;
    meetingDateDetail: MeetingDateDetailResponse;
    occupationAllUnit: OccupationAllUnitResult[];
    realtyAll: RealtyAllResult[];
}

const initialStateModel: MeetingDateStateModel = {
    loadingMeetingDateDetail: false,
    loadingOccupationAllUnit: false,
    loadingRealtyAll: false,
    loadingInsertMeetingDate: false,
    loadingDeleteMeetingDate: false,
    loadingUpdateMeetingDate: false,
    meetingDateDetail: new MeetingDateDetailResponse(),
    occupationAllUnit: [],
    realtyAll: []
};

@State<MeetingDateStateModel>({
    name: 'meetingDate',
    defaults: initialStateModel
})
export class MeetingDateState {
    @Selector()
    static getMeetingDateDetail(state: MeetingDateStateModel) {
        return state.meetingDateDetail;
    }

    @Selector()
    static getOccupationAllUnit(state: MeetingDateStateModel) {
        return state.occupationAllUnit;
    }

    @Selector()
    static getRealtyAll(state: MeetingDateStateModel) {
        return state.realtyAll;
    }

    @Selector()
    static isLoading(state: MeetingDateStateModel) {
        return state.loadingMeetingDateDetail ||
            state.loadingOccupationAllUnit ||
            state.loadingRealtyAll ||
            state.loadingInsertMeetingDate ||
            state.loadingUpdateMeetingDate;
    }

    constructor(
        private webApiService: WebApiService) {
    }

    /**
     * Load meeting date detail
     *
     * @param {StateContext<MeetingDateStateModel>} ctx
     * @param {LoadMeetingDateDetail} action
     * @returns {Observable<any>}
     */
    @Action(LoadMeetingDateDetail)
    loadMeetingDateDetail(ctx: StateContext<MeetingDateStateModel>, action: LoadMeetingDateDetail) {
        ctx.patchState({
            loadingMeetingDateDetail: true
        });

        return this.webApiService.call(
            new MeetingDateDetail(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.id),
            () => new MeetingDateDetailResponse(),
            wsOrganizationUnit
        ).pipe(
            tap((response: MeetingDateDetailResponse[]) => {
                ctx.patchState({
                    loadingMeetingDateDetail: false,
                    meetingDateDetail: response[0]
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingMeetingDateDetail: false,
                    meetingDateDetail: initialStateModel.meetingDateDetail
                });

                throw e;
            })
        );
    }

    /**
     * Load all occupations for the Unit
     *
     * @param {StateContext<MeetingDateStateModel>} ctx
     * @param {LoadOccupationAllUnit} action
     * @returns {Observable<any>}
     */
    @Action(LoadOccupationAllUnit)
    loadOccupationAllUnit(ctx: StateContext<MeetingDateStateModel>, action: LoadOccupationAllUnit) {
        ctx.patchState({
            loadingOccupationAllUnit: true
        });

        return this.webApiService.call(
            new OccupationAllUnit(APP_ID, ApplicationSettings.getString(USER_TOKEN), ApplicationSettings.getNumber(USER_UNIT_ID)),
            () => new OccupationAllUnitResult(),
            wsOrganizationUnit
        ).pipe(
            tap((response: OccupationAllUnitResult[]) => {
                ctx.patchState({
                    loadingOccupationAllUnit: false,
                    occupationAllUnit: response
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingOccupationAllUnit: false,
                    occupationAllUnit: initialStateModel.occupationAllUnit
                });

                throw e;
            })
        );
    }

    /**
     * Load all realties
     *
     * @param {StateContext<MeetingDateStateModel>} ctx
     * @param {LoadRealtyAll} action
     * @returns {Observable<any>}
     */
    @Action(LoadRealtyAll)
    loadRealtyAll(ctx: StateContext<MeetingDateStateModel>, action: LoadRealtyAll) {
        ctx.patchState({
            loadingRealtyAll: true
        });

        return this.webApiService.call(
            new RealtyAll(APP_ID, ApplicationSettings.getString(USER_TOKEN)),
            () => new RealtyAllResult(),
            wsOrganizationUnit
        ).pipe(
            tap((response: RealtyAllResult[]) => {
                ctx.patchState({
                    loadingRealtyAll: false,
                    realtyAll: response
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingRealtyAll: false,
                    realtyAll: initialStateModel.realtyAll
                });

                throw e;
            })
        );
    }

    /**
     * Insert new meeting date
     *
     * @param {StateContext<MeetingDateStateModel>} ctx
     * @param {InsertMeetingDate} action
     * @returns {Observable<any>}
     */
    @Action(InsertMeetingDate)
    insertMeetingDate(ctx: StateContext<MeetingDateStateModel>, action: InsertMeetingDate) {
        ctx.patchState({
            loadingInsertMeetingDate: true
        });

        return this.webApiService.call(
            new MeetingDateInsert(
                APP_ID,
                ApplicationSettings.getString(USER_TOKEN),
                ApplicationSettings.getNumber(USER_UNIT_ID),
                action.weekDayId,
                action.weekDay,
                action.timeFrom,
                action.timeTo,
                action.periodicity,
                action.occupationId),
            () => new BaseOutput(),
            wsOrganizationUnit
        ).pipe(
            tap((response) => {
                ctx.patchState({
                    loadingInsertMeetingDate: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingInsertMeetingDate: false
                });

                throw e;
            })
        );
    }

    /**
     * Delete existing meeting date
     *
     * @param {StateContext<MeetingDateStateModel>} ctx
     * @param {DeleteMeetingDate} action
     * @returns {Observable<any>}
     */
    @Action(DeleteMeetingDate)
    deleteMeetingDate(ctx: StateContext<MeetingDateStateModel>, action: DeleteMeetingDate) {
        ctx.patchState({
            loadingDeleteMeetingDate: true
        });

        return this.webApiService.call(
            new MeetingDateDelete(
                APP_ID,
                ApplicationSettings.getString(USER_TOKEN),
                action.id),
            () => new BaseOutput(),
            wsOrganizationUnit
        ).pipe(
            tap(() => {
                ctx.patchState({
                    loadingDeleteMeetingDate: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingDeleteMeetingDate: false
                });

                throw e;
            })
        );
    }

    /**
     * Update existing meeting date
     *
     * @param {StateContext<MeetingDateStateModel>} ctx
     * @param {UpdateMeetingDate} action
     * @returns {Observable<any>}
     */
    @Action(UpdateMeetingDate)
    updateMeetingDate(ctx: StateContext<MeetingDateStateModel>, action: UpdateMeetingDate) {
        ctx.patchState({
            loadingUpdateMeetingDate: true
        });

        return this.webApiService.call(
            new MeetingDateUpdate(
                APP_ID,
                ApplicationSettings.getString(USER_TOKEN),
                ApplicationSettings.getNumber(USER_UNIT_ID),
                action.ID,
                action.weekDayId,
                action.weekDay,
                action.timeFrom,
                action.timeTo,
                action.periodicity,
                action.occupationId),
            () => new BaseOutput(),
            wsOrganizationUnit
        ).pipe(
            tap((response) => {
                ctx.patchState({
                    loadingUpdateMeetingDate: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingUpdateMeetingDate: false
                });

                throw e;
            })
        );
    }
}
