import { Component, OnInit, ViewChild, AfterViewInit, ElementRef, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { actionsExecuting, ActionsExecuting } from '@ngxs-labs/actions-executing';
import { Select, Store } from '@ngxs/store';
import { RouterExtensions } from '@nativescript/angular';
import { Observable } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
import { LoadRegions } from '~/pages/event/state-mgmt/gen/gen-event-new.actions';
import { GenEventNewState } from '~/pages/event/state-mgmt/gen/gen-event-new.state';
import { SearchUnits } from '~/pages/event/state-mgmt/gen/gen-event-new.actions';
import { action } from '@nativescript/core/ui/dialogs';
import { Feedback, FeedbackType } from 'nativescript-feedback';
import { RegionOutput, UnitOutput } from '~/pages/event/entities/gen-event-new.types';
import { UnitSearchCriteria, UnitSearchOptions } from '~/pages/event/types/UnitSearch';
import { Picker, formatPickerValues, setInputDateFormat } from '~/pages/event/util';
import { LoadCampPlaceAll, LoadCampTypes, LoadUnitAllCamp, NewCampEvent, UpdateCampEvent } from '~/pages/event/state-mgmt/camp/camp-event-new.actions';
import { LoadCampEventDetail, LoadCampUnits } from '~/pages/event/state-mgmt/camp/camp-event-detail.actions';
import { CampEventNewState } from '~/pages/event/state-mgmt/camp/camp-event-new.state';
import { CampPlaceOutput, CampTypeOutput, UnitCampOutput } from '~/pages/event/entities/camp-event-new.types';
import { UnitService } from '~/shared/services/unit.service';
import { UnitTreeAllOutput } from '~/pages/unit/entities/output/unitTreeAllOutput';
import * as dialogs from "@nativescript/core/ui/dialogs";
import { RadDataFormComponent } from 'nativescript-ui-dataform/angular';
import { CampEventDetailState } from '~/pages/event/state-mgmt/camp/camp-event-detail.state';
import { CampEventDetailOutput } from '~/pages/event/entities/camp-event-detail.types';
import { UnitDetailOutput } from '~/pages/unit/entities/output/unitDetailOutput';
import { transitionOptions } from '~/constants/shared.constants';
import { CoordinateType, dec2deg, deg2dec, LATITUDE_REGEX, validateCoordinates } from '~/shared/pipes/gps-coordinate.pipe';
import icons from '~/constants/icons';
import { dismissSoftKeybaord } from './../../../../../core/utils/dismiss-keyboard';
import { GpsFormComponent } from '~/pages/event/components/common/gps-form/gps-form.component';
import { ScrollView } from '@nativescript/core/ui/scroll-view';
import { AndroidApplication, AndroidActivityBackPressedEventData } from '@nativescript/core';
// @ts-ignore
import { android } from 'application';
export interface CampEventNewForm {
    name: string;
    startDate: string;
    endDate: string;
    mobile: string;
    note: string;
}

interface LocationForm {
    regionText: string;
    region: string;
    location: string;
    campPlace: string;
    postCode: string;
    isFloodArea: boolean;

}
const defState: CampEventNewForm = {
    name: '',
    startDate: '',
    endDate: '',
    mobile: '',
    note: '',
}

const defLocationForm = {
    campPlace: '',
    region: '',
    location: '',
    postCode: '',
    isFloodArea: false,
    regionText: '',

}

enum CampType {
    zahranicni = 'zahranicni',
    putovni = 'putovni',
    vodacky = 'vodacky',
}

@Component({
    selector: 'CampEventNewPage',
    moduleId: module.id,
    templateUrl: './camp-event-new-page.component.html',
    styleUrls: ['./_camp-event-new-page.component.scss']
})
export class CampEventNewPageComponent implements OnInit, AfterViewInit, OnDestroy {
    @ViewChild("campEventForm", { static: true })
    form: RadDataFormComponent;
    @ViewChild("locationForm", { static: false })
    locationFormComponent: RadDataFormComponent;

    @ViewChild('scrollview', { static: false })
    scrollView: ElementRef<ScrollView>;


    @ViewChild("gpsForm", { static: true })
    gpsForm: GpsFormComponent;

    subUnitForm = { text: '' };
    @Select(CampEventDetailState.getCampUnits)
    campUnits$: Observable<UnitDetailOutput[]>;

    selectedSubUnits = new Set<string>()

    @Select(actionsExecuting([
        LoadCampTypes, LoadRegions, LoadCampEventDetail,
        LoadCampPlaceAll, LoadUnitAllCamp, LoadCampUnits,
        NewCampEvent, UpdateCampEvent, SearchUnits
    ]))
    isLoading$: Observable<ActionsExecuting>;
    @Select(CampEventNewState.getTypes)
    eventTypes$: Observable<CampTypeOutput[]>;
    types: CampTypeOutput[];
    @Select(CampEventNewState.getUnits)
    subUnits$: Observable<UnitOutput[]>;
    subUnits: UnitOutput[];
    @Select(GenEventNewState.getRegions)
    regions$: Observable<RegionOutput[]>;
    regions: RegionOutput[];
    @Select(CampEventNewState.getPlaces)
    places$: Observable<CampPlaceOutput[]>;
    places: CampPlaceOutput[];
    @Select(CampEventNewState.getCampUnits)
    units$: Observable<UnitCampOutput[]>;
    units: UnitCampOutput[];
    @Select(CampEventNewState.getNewEventId)
    newEventId$: Observable<number>;
    @Select(CampEventDetailState.getEventDetail)
    baseEventData$: Observable<CampEventDetailOutput>;
    baseEventData: CampEventDetailOutput

    updateEventId: number;

    formattedRegions: Picker[] = [{ key: '', label: 'Vyberte' }];
    formattedSubUnits: Picker[] = [{ key: '', label: 'Žádná data' }];
    formattedUnits: Picker[] = [];

    newEventForm: CampEventNewForm;
    locationForm: LocationForm;
    formattedTypes: Picker[] = [{ key: null, label: '' }];
    formattedPlaces: Picker[] = [{ key: '', label: 'Vyberte' }];
    unitForm = { unit: '' };

    selUnit: UnitOutput;
    feedback: Feedback;
    childUnits$: Observable<UnitTreeAllOutput[]>
    formattedOtherUnits: Picker[] = [];
    foundUnits: UnitOutput[] = []
    typeForm: { [key: string]: boolean };
    formUpdating: boolean = false;

    _location = false;
    icons = icons;

    public get location(): boolean {
        return this._location;
    }

    // so the spinner starts spinning and the ui reloads correctly,
    // the cdr is used because somehow the ui doesnt reload when we use android backbutton
    public set location(v: boolean) {
        this.formUpdating = true;
        this.cdr.detectChanges();
        setTimeout(() => this._location = v, 1)
        setTimeout(() => {
            this.formUpdating = false;
            this.cdr.detectChanges();
            this.scrollView.nativeElement.scrollToVerticalOffset(0, true);
        }, 250);
    }


    constructor(
        private routerExtensions: RouterExtensions,
        private route: ActivatedRoute,
        private store: Store,
        private unitService: UnitService,
        private cdr: ChangeDetectorRef,
    ) {
        this.feedback = new Feedback();
    }
    ngOnDestroy(): void {
        android.off(AndroidApplication.activityBackPressedEvent, this.eventListener);
    }

    ngAfterViewInit(): void {
        setInputDateFormat(this.form, ['startDate', 'endDate']);
    }

    defVal = 'Vyberte';
    subUnitIds = [];

    eventListener = (data: AndroidActivityBackPressedEventData) => {
        if (this.location) {
            this.location = false;
            data.cancel = true;
        }
        return data;
    }

    loadSubUnits(unitId: number) {
        return this.unitService.getUnitTree(unitId).toPromise().then(res => {
            this.formattedSubUnits = res.map(p => ({ key: p.ID_Unit.toString(), label: p.RegistrationNumber + ' - ' + p.Unit }));
            return res;
        })
    }
    onSelectedUnitChange() {
        this.unitForm.unit && this.loadSubUnits(parseInt(this.unitForm.unit))
    }

    selectSubUnit(item: Picker) {
        const id = item.key
        if (this.selectedSubUnits.has(id))
            this.selectedSubUnits.delete(id)
        else
            this.selectedSubUnits.add(id)

    }
    ngOnInit(): void {
        this.newEventForm = { ...defState };
        this.locationForm = { ...defLocationForm };
        this.route.params.subscribe(params => {
            if (params['id']) {
                this.updateEventId = parseInt(params['id'], 10);
            }
        });
        this.store.dispatch([
            new LoadCampTypes(), new LoadRegions(),
            new LoadCampPlaceAll(), new LoadUnitAllCamp()])
            .pipe(withLatestFrom(this.eventTypes$, this.regions$, this.places$, this.units$))
            .subscribe(([_, types, regions, places, units]) => {
                this.units = units;

                this.types = types;
                this.formattedRegions = formatPickerValues(regions, 'Vyberte');
                this.formattedPlaces = formatPickerValues(places, 'Vyberte')

                if (this.updateEventId) {
                    this.store.dispatch(new LoadCampEventDetail(this.updateEventId))
                        .pipe(withLatestFrom(this.baseEventData$)).subscribe(([_, res]) => {
                            this.baseEventData = res;
                            this.formattedUnits = [{
                                label: res.RegistrationNumber + ' - ' + res.Unit,
                                key: res.ID_Unit.toString()
                            }]
                            this.typeForm = this.types.reduce((acc, curr) => {
                                acc[curr.ID] = res.CampType.includes(curr.DisplayName);
                                return acc;
                            }, {})
                            const sDate = new Date(res.StartDate)
                            const eDate = new Date(res.EndDate)
                            this.loadSubUnits(res.ID_Unit).then((units) => {
                                setTimeout(() => {
                                    this.unitForm = { unit: res.ID_Unit.toString() }
                                    const otherUnits: string[] = []
                                    res.ID_UnitArray.forEach(u => {
                                        const found = units.find(i => i.ID_Unit === parseInt(u))
                                        if (found) {
                                            this.selectedSubUnits.add(found.ID_Unit.toString())
                                        } else {
                                            otherUnits.push(u)
                                        }
                                    })
                                    this.store.dispatch(new LoadCampUnits(otherUnits))
                                        .pipe(withLatestFrom(this.campUnits$))
                                        .subscribe(([_, units]) => {
                                            this.formattedOtherUnits = units.map(u =>
                                            ({
                                                key: u.ID.toString(),
                                                label: u.RegistrationNumber + ' - ' + u.DisplayName
                                            }))
                                        });

                                }, 10)
                            });

                            this.newEventForm = {
                                name: res.DisplayName,
                                startDate: sDate.toISOString(),
                                endDate: eDate.toISOString(),
                                mobile: res.MobileContact,
                                note: res.Note,
                            };
                            this.locationForm = {
                                campPlace: res.ID_CampPlace ? res.ID_CampPlace.toString() : '',
                                region: res.ID_Region ? res.ID_Region.toString() : '',
                                location: res.Location,
                                postCode: res.Postcode,
                                isFloodArea: res.IsFloodArea,
                                regionText: res.Region
                            }
                            this.gpsForm.formData = {
                                gpsLatitude: dec2deg(res.GpsLatitude, CoordinateType.LATITUDE),
                                gpsLongitude: dec2deg(res.GpsLongitude, CoordinateType.LONGITUDE),
                            };
                        })
                } else {
                    this.formattedUnits = formatPickerValues(units.map(o => ({ DisplayName: o.SortName, ID: o.ID })), '')
                    if (units.length) {
                        this.loadSubUnits(units[0].ID)
                    }
                    this.newEventForm = {
                        ...defState,
                    }
                    this.typeForm = types.reduce((acc, curr) => { acc[curr.ID] = false; return acc; }, {})
                }
            });


        if (android) {
            this.eventListener = android.on(AndroidApplication.activityBackPressedEvent, this.eventListener);
        }
    }

    onBackButton(): void {
        if (this.location) {
            this.location = false;
        } else {
            this.routerExtensions.back();
        }
    }

    onSearchClick() {
        const storno = "Storno";
        let options = {
            title: "Vyhledat podle",
            message: "Vyber",
            cancelButtonText: storno,
            actions: UnitSearchCriteria.map(v => v.label)
        };

        action(options).then((result) => {
            if (result === storno) return;
            const selectedCrit = UnitSearchCriteria.find(c => c.label === result)
            dismissSoftKeybaord()
            const text = this.subUnitForm.text;
            const presentUnits = new Set(
                [...this.formattedOtherUnits.map(u => u.key),
                ...Array.from(this.selectedSubUnits)]);

            this.store.dispatch(new SearchUnits({ criteria: UnitSearchOptions[selectedCrit.key], text }, 'oddil'))
                .pipe(withLatestFrom(this.subUnits$))
                .subscribe(([_, units]) => {
                    this.foundUnits = units.filter(u => !presentUnits.has(u.ID.toString()));
                    if (this.foundUnits.length === 0) {
                        dialogs.alert('Oddíly nebyly nalezeni')
                        return;
                    }
                    dialogs.action({
                        title: 'Vyberte oddíl',
                        message: "Vyberte vyhledaný oddíl",
                        cancelButtonText: "Zrušit",
                        actions: this.foundUnits.map(p => p.SortName)
                    }).then((result) => {
                        const { SortName, ID } = this.foundUnits.find(m => m.SortName === result);
                        this.formattedOtherUnits.push({ key: ID.toString(), label: SortName })

                    });
                })
        });
    }

    removeUnit(item: Picker) {
        this.formattedOtherUnits = this.formattedOtherUnits.filter(i => i.key !== item.key)
    }

    onSelectedTypeChange = (item: CampTypeOutput) => {
        this.typeForm = { ...this.typeForm, [item.ID]: !this.typeForm[item.ID] };
    }

    async validate() {
        const {
            name, startDate, endDate,
        } = this.newEventForm;
        const { region, campPlace,
            location, postCode, regionText, } = this.locationForm;
        const { gpsLatitude, gpsLongitude } = this.gpsForm.formData;
        const isValid = await this.form.dataForm.validateAndCommitAll()
        if (!isValid) return false

        const required = [name, startDate, endDate, this.unitForm.unit,
            location, gpsLongitude, gpsLatitude]
        const requiredNames = ['Název', 'Datum začátku', 'Datum konce',
            'Pořadatele', 'Katastrální území', 'Zeměpisnou délku', "Zeměpisnou šířku"]
            .map((val, i) => ({ name: val, value: required[i] }))
        const res = requiredNames.every(a => a.value);
        let errMessage = null;
        if (!res)
            errMessage = 'Zadejte ' + requiredNames
                .filter(f => !f.value)
                .map(f => f.name).join(', ')
        if (new Date(startDate) > new Date(endDate)) {
            errMessage = 'Konec akce nemůže být dříve než její začátek.';
        }
        if (this.typeForm[CampType.zahranicni] === false
            && this.typeForm[CampType.putovni] === false) {
            if (!region || region === this.defVal)
                errMessage = 'Zvolte kraj.'
            else if (!postCode)
                errMessage = 'Zvolte PSČ obce.'
        }
        if (this.typeForm[CampType.zahranicni] === true
            || this.typeForm[CampType.putovni] === true) {
            if (!regionText)
                errMessage = 'Zadejte název kraje.'
        }
        if (this.typeForm[CampType.putovni] === false) {
            if (!campPlace || campPlace === this.defVal)
                errMessage = 'Zvolte místo tábora.'
        }
        if (!validateCoordinates(deg2dec(gpsLatitude), deg2dec(gpsLongitude)))
            errMessage = `GPS souřadnice jsou špatně (${!LATITUDE_REGEX.test(deg2dec(gpsLatitude)) ? 'šířka' : 'délka'})`
        if (errMessage) {
            this.feedback.show({
                title: 'ERROR',
                type: FeedbackType.Error,
                message: errMessage
            });
            return false;
        }
        return true;
    }

    async submit() {

        const typeIds: string[] = this.types
            .filter(i => this.typeForm[i.ID]).map(i => i.ID)

        const isValid = await this.validate();
        if (!isValid) return

        const { endDate, startDate, } = this.newEventForm;
        const { campPlace, region, regionText } = this.locationForm;
        const { gpsLatitude, gpsLongitude } = this.gpsForm.formData;

        let campPlaceId: number = undefined;
        if (this.typeForm[CampType.putovni] === false && !parseInt(campPlace)) {

            const foundPlace = this.formattedPlaces.find(p => p.key === campPlace || p.label === campPlace)
            campPlaceId = parseInt(foundPlace.key)
        } else {
            campPlaceId = parseInt(campPlace);
        }

        let regionRes = regionText;
        if (!(this.typeForm[CampType.putovni] ||
            this.typeForm[CampType.zahranicni])) {
            const a = this.formattedRegions.find(r => r.key === region || region === r.label)
            regionRes = a.key
        }
        const latNumber = deg2dec(gpsLatitude).toString()
        const lonNumber = deg2dec(gpsLongitude).toString()



        const subUnits = new Set(this.selectedSubUnits);
        this.formattedOtherUnits.forEach(u => subUnits.add(u.key));
        const units: string[] = Array.from(subUnits);
        if (this.updateEventId) {
            this.store.dispatch(new UpdateCampEvent({
                ...this.newEventForm, ...this.locationForm, endDate: new Date(endDate),
                startDate: new Date(startDate), typeIds, subUnitIds: units,
                unitId: parseInt(this.unitForm.unit),
                region: regionRes,
                campPlaceId, gpsLatitude: latNumber, gpsLongitude: lonNumber
            }, this.updateEventId)).subscribe(() => {
                setTimeout(() => {
                    this.routerExtensions.navigate(['event/camp'],
                        {
                            transition: transitionOptions, clearHistory: true,
                            queryParams: { eventId: this.updateEventId }
                        });
                }, 10);
            })

        } else {
            this.store.dispatch(new NewCampEvent({
                ...this.newEventForm, ...this.locationForm, endDate: new Date(endDate),
                startDate: new Date(startDate), typeIds, subUnitIds: units,
                unitId: parseInt(this.unitForm.unit),
                region: regionRes, gpsLatitude, gpsLongitude, campPlaceId,
            })).pipe(withLatestFrom(this.newEventId$)).subscribe(([_, newId]) => {
                setTimeout(() => {
                    this.routerExtensions.navigate(['event/camp'],
                        {
                            transition: transitionOptions, clearHistory: true,
                            queryParams: { eventId: newId }
                        });
                }, 10);
            })
        }
    }
}