import { Action, Selector, State, StateContext } from '@ngxs/store';

import { ApplicationSettings, ImageSource } from '@nativescript/core';
import { getImage } from '@nativescript/core/http';
import { catchError, tap } from 'rxjs/operators';
import { APP_ID, wsTelephony } from '~/constants/api.constants';
import { USER_PERSON_ID, USER_TOKEN } from '~/constants/login-constants';
import { WebApiService } from '~/core/services/web-api.service';
import { EnrollInvoiceAllPerson } from '~/pages/sts/entities/input/enrollInvoiceAllPerson';
import { EnrollInvoiceDetail } from '~/pages/sts/entities/input/enrollInvoiceDetail';
import { EnrollInvoiceItemAll } from '~/pages/sts/entities/input/enrollInvoiceItemAll';
import { EnrollInvoiceServiceAll } from '~/pages/sts/entities/input/enrollInvoiceServiceAll';
import { PaymentAllEnrollInvoice } from '~/pages/sts/entities/input/paymentAllEnrollInvoice';
import { EnrollInvoiceAllPersonOutput } from '~/pages/sts/entities/output/enrollInvoiceAllPersonOutput';
import { EnrollInvoiceDetailOutput } from '~/pages/sts/entities/output/enrollInvoiceDetailOutput';
import { EnrollInvoiceItemAllOutput } from '~/pages/sts/entities/output/enrollInvoiceItemAllOutput';
import { EnrollInvoiceServiceAllOutput } from '~/pages/sts/entities/output/enrollInvoiceServiceAllOutput';
import { PaymentAllEnrollInvoiceOutput } from '~/pages/sts/entities/output/paymentAllEnrollInvoiceOutput';
import {
    ClearQrCodeImageSource,
    LoadEnrollInvoiceAllPayment,
    LoadEnrollInvoiceDetail,
    LoadEnrollInvoiceItemForPhoneNumber,
    LoadEnrollInvoiceItemServices,
    LoadEnrollInvoices,
    LoadQrCodeImageSource,
    StartQrCodeDownloading,
    StopQrCodeDownloading
} from '~/pages/sts/state-mgmt/sts-detail-bill.actions';

export interface StsDetailBillStateModel {
    loadingEnrollInvoices: boolean;
    loadingEnrollInvoiceDetail: boolean;
    loadingEnrollInvoiceItemForPhoneNumber: boolean;
    loadingEnrollInvoiceItemServices: boolean;
    loadingEnrollInvoiceAllPayments: boolean;
    loadingQrCodeImageSource: boolean;
    loadingQrCodeDownloading: boolean;
    enrollInvoices: EnrollInvoiceAllPersonOutput[];
    enrollInvoiceDetail: EnrollInvoiceDetailOutput;
    enrollInvoiceItemForPhoneNumber: EnrollInvoiceItemAllOutput;
    enrollInvoiceItemServices: EnrollInvoiceServiceAllOutput[];
    enrollInvoiceAllPayments: PaymentAllEnrollInvoiceOutput[];
    qrCodeImageSource: ImageSource;
}

const initialStateModel: StsDetailBillStateModel = {
    loadingEnrollInvoices: false,
    loadingEnrollInvoiceDetail: false,
    loadingEnrollInvoiceItemForPhoneNumber: false,
    loadingEnrollInvoiceItemServices: false,
    loadingEnrollInvoiceAllPayments: false,
    loadingQrCodeImageSource: false,
    loadingQrCodeDownloading: false,
    enrollInvoices: [],
    enrollInvoiceDetail: null,
    enrollInvoiceItemForPhoneNumber: null,
    enrollInvoiceItemServices: [],
    enrollInvoiceAllPayments: [],
    qrCodeImageSource: null
};

@State<StsDetailBillStateModel>({
    name: 'stsDetailBill',
    defaults: initialStateModel
})

export class StsDetailBillState {

    @Selector()
    static getEnrollInvoices(state: StsDetailBillStateModel) {
        return state.enrollInvoices;
    }

    @Selector()
    static getEnrollInvoiceDetail(state: StsDetailBillStateModel) {
        return state.enrollInvoiceDetail;
    }

    @Selector()
    static getEnrollInvoiceItemForPhoneNumber(state: StsDetailBillStateModel) {
        return state.enrollInvoiceItemForPhoneNumber;
    }

    @Selector()
    static getEnrollInvoiceItemServices(state: StsDetailBillStateModel) {
        return state.enrollInvoiceItemServices;
    }

    @Selector()
    static getEnrollInvoiceAllPayments(state: StsDetailBillStateModel) {
        return state.enrollInvoiceAllPayments;
    }

    @Selector()
    static getQrCodeImageSource(state: StsDetailBillStateModel) {
        return state.qrCodeImageSource;
    }

    @Selector()
    static isLoading(state: StsDetailBillStateModel) {
        return state.loadingEnrollInvoices
            || state.loadingEnrollInvoiceDetail
            || state.loadingEnrollInvoiceItemForPhoneNumber
            || state.loadingEnrollInvoiceItemServices
            || state.loadingEnrollInvoiceAllPayments
            || state.loadingQrCodeImageSource
            || state.loadingQrCodeDownloading;
    }

    constructor(
        private webApiService: WebApiService
    ) {
    }

    @Action(LoadEnrollInvoices)
    loadEnrollInvoices(ctx: StateContext<StsDetailBillStateModel>, action: LoadEnrollInvoices) {
        ctx.patchState({
            loadingEnrollInvoices: true
        });

        return this.webApiService.call(
            new EnrollInvoiceAllPerson(APP_ID, ApplicationSettings.getString(USER_TOKEN), ApplicationSettings.getNumber(USER_PERSON_ID)),
            () => new EnrollInvoiceAllPersonOutput(),
            wsTelephony
        ).pipe(
            tap((response: EnrollInvoiceAllPersonOutput[]) => {
                ctx.patchState({
                    loadingEnrollInvoices: false,
                    enrollInvoices: response
                        .sort(this.enrollInvoiceAllDateFromSort)
                        .slice(0, 12)
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingEnrollInvoices: false,
                    enrollInvoices: initialStateModel.enrollInvoices
                });

                throw e;
            })
        );
    }

    @Action(LoadEnrollInvoiceDetail)
    loadEnrollInvoiceDetail(ctx: StateContext<StsDetailBillStateModel>, action: LoadEnrollInvoiceDetail) {
        ctx.patchState({
            loadingEnrollInvoiceDetail: true
        });

        return this.webApiService.call(
            new EnrollInvoiceDetail(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.invoiceId),
            () => new EnrollInvoiceDetailOutput(),
            wsTelephony
        ).pipe(
            tap((response: EnrollInvoiceDetailOutput[]) => {
                if (response.length === 1) {
                    ctx.patchState({
                        loadingEnrollInvoiceDetail: false,
                        enrollInvoiceDetail: response[0]
                    });
                } else {
                    ctx.patchState({
                        loadingEnrollInvoiceDetail: false,
                        enrollInvoiceDetail: initialStateModel.enrollInvoiceDetail
                    });
                    throw new Error('Nepodařilo se načíst detail faktury');
                }
            }),
            catchError(e => {
                ctx.patchState({
                    loadingEnrollInvoiceDetail: false,
                    enrollInvoiceDetail: initialStateModel.enrollInvoiceDetail
                });

                throw e;
            })
        );
    }

    /**
     * Load item of enrollInvoice for number and update state. If item was finded, then action for services and calls are dispatch
     *
     * @param {StateContext<StsDetailBillStateModel>} ctx
     * @param {LoadEnrollInvoiceItemForPhoneNumber} action
     * @returns {Observable<any>}
     */
    @Action(LoadEnrollInvoiceItemForPhoneNumber)
    loadEnrollInvoiceItemForPhoneNumber(ctx: StateContext<StsDetailBillStateModel>, action: LoadEnrollInvoiceItemForPhoneNumber) {
        ctx.patchState({
            loadingEnrollInvoiceItemForPhoneNumber: true
        });

        if (action.invoiceId) {
            return this.webApiService.call(
                new EnrollInvoiceItemAll(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.invoiceId),
                () => new EnrollInvoiceItemAllOutput(),
                wsTelephony
            ).pipe(
                tap((response: EnrollInvoiceItemAllOutput[]) => {
                    const enrollInvoiceItemForPhoneNumber: EnrollInvoiceItemAllOutput = response
                        .find((invoiceItem: EnrollInvoiceItemAllOutput) => invoiceItem.EnrollNumber === action.phoneNumber);

                    if (enrollInvoiceItemForPhoneNumber) {
                        ctx.patchState({
                            loadingEnrollInvoiceItemForPhoneNumber: false,
                            enrollInvoiceItemForPhoneNumber
                        });
                        ctx.dispatch(new LoadEnrollInvoiceItemServices(enrollInvoiceItemForPhoneNumber.ID));
                    } else {
                        ctx.patchState({
                            loadingEnrollInvoiceItemForPhoneNumber: false,
                            enrollInvoiceItemForPhoneNumber: initialStateModel.enrollInvoiceItemForPhoneNumber,
                            enrollInvoiceItemServices: initialStateModel.enrollInvoiceItemServices
                        });
                        throw new Error('Nebyla nalezena položka ve faktuře pro dané číslo');
                    }
                }),
                catchError(e => {
                    ctx.patchState({
                        loadingEnrollInvoiceItemForPhoneNumber: false,
                        enrollInvoiceItemForPhoneNumber: initialStateModel.enrollInvoiceItemForPhoneNumber,
                        enrollInvoiceItemServices: initialStateModel.enrollInvoiceItemServices
                    });

                    throw e;
                })
            );
        } else {
            ctx.patchState({
                loadingEnrollInvoiceItemForPhoneNumber: false
            });
        }
    }

    @Action(LoadEnrollInvoiceItemServices)
    loadEnrollInvoiceItemServices(ctx: StateContext<StsDetailBillStateModel>, action: LoadEnrollInvoiceItemServices) {
        ctx.patchState({
            loadingEnrollInvoiceItemServices: true
        });

        if (action.invoiceItemId) {
            return this.webApiService.call(
                new EnrollInvoiceServiceAll(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.invoiceItemId),
                () => new EnrollInvoiceServiceAllOutput(),
                wsTelephony
            ).pipe(
                tap((response: EnrollInvoiceServiceAllOutput[]) => {
                    ctx.patchState({
                        loadingEnrollInvoiceItemServices: false,
                        enrollInvoiceItemServices: response
                    });
                }),
                catchError(e => {
                    ctx.patchState({
                        loadingEnrollInvoiceItemServices: false,
                        enrollInvoiceItemServices: initialStateModel.enrollInvoiceItemServices
                    });

                    throw e;
                })
            );
        } else {
            ctx.patchState({
                loadingEnrollInvoiceItemServices: false
            });
        }
    }

    @Action(LoadEnrollInvoiceAllPayment)
    loadEnrollInvoiceAllPayment(ctx: StateContext<StsDetailBillStateModel>, action: LoadEnrollInvoiceAllPayment) {
        ctx.patchState({
            loadingEnrollInvoiceAllPayments: true
        });

        if (action.invoiceId) {
            return this.webApiService.call(
                new PaymentAllEnrollInvoice(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.invoiceId),
                () => new PaymentAllEnrollInvoiceOutput(),
                wsTelephony
            ).pipe(
                tap((response: PaymentAllEnrollInvoiceOutput[]) => {
                    ctx.patchState({
                        loadingEnrollInvoiceAllPayments: false,
                        enrollInvoiceAllPayments: response
                    });
                }),
                catchError(e => {
                    ctx.patchState({
                        loadingEnrollInvoiceAllPayments: false,
                        enrollInvoiceAllPayments: initialStateModel.enrollInvoiceAllPayments
                    });

                    throw e;
                })
            );
        } else {
            ctx.patchState({
                loadingEnrollInvoiceAllPayments: false
            });
        }
    }

    @Action(LoadQrCodeImageSource)
    loadQrCodeImageSource(ctx: StateContext<StsDetailBillStateModel>, action: LoadQrCodeImageSource) {
        ctx.patchState({
            loadingQrCodeImageSource: true
        });

        if (action.accountNumber && action.amount && action.vs) {
            const acount = action.accountNumber.split('/');
            getImage(`https://api.paylibo.com/paylibo/generator/czech/image?accountNumber=${acount[0]}&bankCode=${acount[1]}&amount=${action.amount}&currency=CZK&vs=${action.vs}&ks=0308&recipientName=${encodeURI('Junak - cesky skaut, TDC')}`)
                .then(
                    (data: ImageSource) => {
                        ctx.patchState({
                            loadingQrCodeImageSource: false,
                            qrCodeImageSource: data
                        });
                    }
                )
                .catch(e => {
                    ctx.patchState({
                        loadingQrCodeImageSource: false,
                        qrCodeImageSource: initialStateModel.qrCodeImageSource
                    });

                    throw e;
                });
        } else {
            ctx.patchState({
                loadingQrCodeImageSource: false
            });
        }
    }

    @Action(ClearQrCodeImageSource)
    clearQrCodeImageSource(ctx: StateContext<StsDetailBillStateModel>, action: ClearQrCodeImageSource) {
        ctx.patchState({
            qrCodeImageSource: initialStateModel.qrCodeImageSource
        });
    }

    @Action(StartQrCodeDownloading)
    startQrCodeDownloading(ctx: StateContext<StsDetailBillStateModel>, action: StartQrCodeDownloading) {
        ctx.patchState({
            loadingQrCodeDownloading: true
        });
    }

    @Action(StopQrCodeDownloading)
    stopQrCodeDownloading(ctx: StateContext<StsDetailBillStateModel>, action: StopQrCodeDownloading) {
        ctx.patchState({
            loadingQrCodeDownloading: initialStateModel.loadingQrCodeDownloading
        });
    }

    /**
     * Sorter of EnrollInvoiceAllPersonOutput. It parse yeary, month, day, hour, minutes and seconds from input string.
     * Return -1, 0 , 1 according to which is newest. It sorting descending.
     *
     * @param {EnrollInvoiceAllPersonOutput} a
     * @param {EnrollInvoiceAllPersonOutput} b
     * @returns {number}
     */
    enrollInvoiceAllDateFromSort(a: EnrollInvoiceAllPersonOutput, b: EnrollInvoiceAllPersonOutput): number {
        let dateFromA = a.DateFrom;
        let dateFromB = b.DateFrom;
        dateFromA = (dateFromA.lastIndexOf('.') !== -1) ? dateFromA.substr(0, dateFromA.lastIndexOf('.')) : dateFromA;
        dateFromB = (dateFromB.lastIndexOf('.') !== -1) ? dateFromB.substr(0, dateFromB.lastIndexOf('.')) : dateFromB;
        const [dateA, timeA] = dateFromA.split('T');
        const [dateB, timeB] = dateFromB.split('T');

        const [yearA, monthA, dayA] = dateA.split('-');
        const [hourA, minutesA, secondsA] = timeA.split(':');
        const [yearB, monthB, dayB] = dateB.split('-');
        const [hourB, minutesB, secondsB] = timeB.split(':');

        if (yearA > yearB) {
            return -1;
        } else if (yearA < yearB) {
            return 1;
        } else {
            if (monthA > monthB) {
                return -1;
            } else if (monthA < monthB) {
                return 1;
            } else {
                if (dayA > dayB) {
                    return -1;
                } else if (dayA < dayB) {
                    return 1;
                }

                if (hourA > hourB) {
                    return -1;
                } else if (hourA < hourB) {
                    return 1;
                } else {
                    if (minutesA > minutesB) {
                        return -1;
                    } else if (minutesA < minutesB) {
                        return 1;
                    } else {
                        if (secondsA > secondsB) {
                            return -1;
                        } else if (secondsA < secondsB) {
                            return 1;
                        } else {
                            return 0;
                        }
                    }
                }
            }
        }
    }

}
