import { ApplicationSettings } from '@nativescript/core';
import { Action, Selector, State, StateContext } from '@ngxs/store';
import { catchError, tap } from 'rxjs/operators';
import { APP_ID, wsReports, wsSummary } from '~/constants/api.constants';
import { USER_TOKEN } from '~/constants/login-constants';
import { WebApiService } from '~/core/services/web-api.service';
import { Column } from '../entities/column';
import { ExportTableData } from '../entities/export-table-data';
import { ExportPerson } from '../entities/input/exportPerson';
import { ExportPersonView } from '../entities/input/exportPersonView';
import { ExportPersonResult } from '../entities/output/exportPersonResult';
import { ExportPersonViewResult } from '../entities/output/exportPersonViewResult';
import { LoadEmails, LoadExportPerson, LoadExportPersonView, LoadPhoneNumbers } from './export.actions';

export interface ExportStateModel {
    loadingExportTableData: boolean;
    loadingPhoneNumbers: boolean;
    loadingEmails: boolean;
    loadingExportPerson: boolean;
    exportTableData: ExportTableData;
    phoneNumbers: Object;
    emails: Object;
    exportPerson: ExportPersonResult[];
}

const initialStateModel: ExportStateModel = {
    loadingExportTableData: false,
    loadingPhoneNumbers: false,
    loadingEmails: false,
    loadingExportPerson: false,
    exportTableData: new ExportTableData([], [], []),
    phoneNumbers: {},
    emails: {},
    exportPerson: []
};

@State<ExportStateModel>({
    name: 'export',
    defaults: initialStateModel
})
export class ExportState {
    @Selector()
    static getExportTableData(state: ExportStateModel) {
        return state.exportTableData;
    }

    @Selector()
    static getPhoneNumbers(state: ExportStateModel) {
        return state.phoneNumbers;
    }

    @Selector()
    static getEmails(state: ExportStateModel) {
        return state.emails;
    }

    @Selector()
    static getExportPerson(state: ExportStateModel) {
        return state.exportPerson;
    }

    @Selector()
    static isLoading(state: ExportStateModel) {
        return state.loadingExportTableData || state.loadingPhoneNumbers || state.loadingEmails || state.loadingExportPerson;
    }

    constructor(
        private webApiService: WebApiService) {
    }

    /**
     * Load all exported people's data based on the template
     *
     * @param {StateContext<ExportStateModel>} ctx
     * @param {LoadExportPersonView} action
     * @returns {Observable<any>}
     */
    @Action(LoadExportPersonView)
    loadExportPersonView(ctx: StateContext<ExportStateModel>, action: LoadExportPersonView) {
        ctx.patchState({
            loadingExportTableData: true
        });

        return this.webApiService.call(
            new ExportPersonView(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.idTemplate),
            () => new ExportPersonViewResult(),
            wsSummary
        ).pipe(
            tap((response: ExportPersonViewResult[]) => {
                ctx.patchState({
                    exportTableData: this.parseData(response[0].Data, this.parseColumns(response[0].Columns)),
                    loadingExportTableData: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingExportTableData: false,
                    exportTableData: initialStateModel.exportTableData
                });

                throw e;
            })
        );
    }

    /**
     * Load all people's phone numbers
     *
     * @param {StateContext<ExportStateModel>} ctx
     * @param {LoadPhoneNumbers} action
     * @returns {Observable<any>}
     */
    @Action(LoadPhoneNumbers)
    loadPhoneNumbers(ctx: StateContext<ExportStateModel>, action: LoadPhoneNumbers) {
        const contactsTemplateId: number = 11;

        ctx.patchState({
            loadingPhoneNumbers: true
        });

        return this.webApiService.call(
            new ExportPersonView(APP_ID, ApplicationSettings.getString(USER_TOKEN), contactsTemplateId),
            () => new ExportPersonViewResult(),
            wsSummary
        ).pipe(
            tap((response: ExportPersonViewResult[]) => {
                ctx.patchState({
                    phoneNumbers: this.parseByAttribute(response[0].Data, 'Contact_telefon_hlavni'),
                    loadingPhoneNumbers: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingPhoneNumbers: false,
                    phoneNumbers: initialStateModel.phoneNumbers
                });

                throw e;
            })
        );
    }

    /**
     * Load all people's phone emails
     *
     * @param {StateContext<ExportStateModel>} ctx
     * @param {LoadEmails} action
     * @returns {Observable<any>}
     */
    @Action(LoadEmails)
    loadEmails(ctx: StateContext<ExportStateModel>, action: LoadEmails) {
        const contactsTemplateId: number = 11;

        ctx.patchState({
            loadingEmails: true
        });

        return this.webApiService.call(
            new ExportPersonView(APP_ID, ApplicationSettings.getString(USER_TOKEN), contactsTemplateId),
            () => new ExportPersonViewResult(),
            wsSummary
        ).pipe(
            tap((response: ExportPersonViewResult[]) => {
                ctx.patchState({
                    emails: this.parseByAttribute(response[0].Data, 'Contact_email_hlavni'),
                    loadingEmails: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingEmails: false,
                    emails: initialStateModel.emails
                });

                throw e;
            })
        );
    }

    /**
     * Load export file (pdf...)
     *
     * @param {StateContext<ExportStateModel>} ctx
     * @param {LoadExportPerson} action
     * @returns {Observable<any>}
     */
    @Action(LoadExportPerson)
    loadExportPerson(ctx: StateContext<ExportStateModel>, action: LoadExportPerson) {
        const fileFormat = 'PDF';

        ctx.patchState({
            loadingExportPerson: true
        });

        return this.webApiService.call(
            new ExportPerson(APP_ID, ApplicationSettings.getString(USER_TOKEN), action.idTemplate, fileFormat),
            () => new ExportPersonResult(),
            wsReports
        ).pipe(
            tap((response: ExportPersonResult[]) => {
                ctx.patchState({
                    exportPerson: response,
                    loadingExportPerson: false
                });
            }),
            catchError(e => {
                ctx.patchState({
                    loadingExportPerson: false,
                    exportPerson: initialStateModel.exportPerson
                });

                throw e;
            })
        );
    }

    private parseData(data: string, columns: Column[]): ExportTableData {
        const parseString = require('nativescript-xml2js').parseString;
        const values = [];
        const ids = [];
        const persons = [];
        parseString(data, (err, result) => {
            const obj = JSON.parse(JSON.stringify(result));
            columns.forEach(col => {
                values.push([col.DisplayName]);
            });
            let attributesIndex;
            if (!obj.persons.row) {
                return new ExportTableData([], [], []);
            }
            obj.persons.row.forEach(person => {
                attributesIndex = 0;
                columns.forEach(col => {
                    if (person[col.Name] && person[col.Name] !== '') {
                        values[attributesIndex].push(person[col.Name][0]);
                    } else {
                        values[attributesIndex].push('---');
                    }
                    attributesIndex += 1;
                });
                let id, firstName, lastName, displayName;
                if (person['ID']) {
                    id = person['ID'][0];
                }
                if (person['FirstName']) {
                    firstName = person['FirstName'][0];
                }
                if (person['LastName']) {
                    lastName = person['LastName'][0];
                }
                if (person['DisplayName']) {
                    displayName = person['DisplayName'][0];
                }

                if (id) {
                    ids.push(id);
                } else {
                    ids.push(null);
                }
                if (!displayName && firstName && lastName) {
                    displayName = firstName + ' ' + lastName;
                }
                if (displayName) {
                    persons.push(displayName);
                } else {
                    if (id) {
                        persons.push(id + ' (id)');
                    } else {
                        persons.push('');
                    }
                }
            });
        });

        return new ExportTableData(values, ids, persons);
    }

    private parseColumns(columns: string): Column[] {
        const parseString = require('nativescript-xml2js').parseString;
        const cols = [];
        parseString(columns, (err, result) => {
            const obj = JSON.parse(JSON.stringify(result));
            if (obj.Columns.PersonExportColumn.length > 0) {
                obj.Columns.PersonExportColumn.forEach(col => {
                    cols.push(new Column(col.Name.toString(), col.DisplayName.toString()));
                });
            }
        });

        return cols;
    }

    private parseByAttribute(data: string, attribute: string): Object {
        const dict = {};
        const parseString = require('nativescript-xml2js').parseString;
        parseString(data, (err, result) => {
            const obj = JSON.parse(JSON.stringify(result));
            obj.persons.row.forEach(person => {
                if (person['ID'] && person[attribute] && person[attribute][0] !== '') {
                    dict[person['ID']] = attribute === 'Contact_telefon_hlavni' ? '+420'.concat(person[attribute][0]) : person[attribute][0];
                }
            });
        });

        return dict;
    }
}
