import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
import {CacheService} from './cache.service';
import {Router} from '@angular/router';
import {HttpService} from './http.service';
import {IApiResponse} from '../model/virtual/api-response';
import {IAppServiceData} from '../model/virtual/app-service-data';
import {isNull} from 'util';
import {saveAs} from 'file-saver';
import {ServiceHelper} from '../utils/service-helper';
import {ICustomerNote} from '../model/entities/customer-note';
import {ICustomerMeeting} from '../model/entities/customer-meeting';
import {IInternalMeeting} from '../model/entities/internal-meeting';
import {IMeetingData} from '../model/virtual/meeting-data';
import {IOperatorMeeting} from '../model/entities/operator-meeting';
import {IProductionMeeting} from '../model/entities/production-meeting';
import {IServiceMeeting} from '../model/entities/service-meeting';

@Injectable()
export class MeetingService {

    private customerMeetings: BehaviorSubject<ICustomerMeeting[]>;
    private customerMeetings$: Observable<ICustomerMeeting[]>;
    private internalMeetings: BehaviorSubject<IInternalMeeting[]>;
    private internalMeetings$: Observable<IInternalMeeting[]>;
    private operatorMeetings: BehaviorSubject<IOperatorMeeting[]>;
    private operatorMeetings$: Observable<IOperatorMeeting[]>;
    private productionMeetings: BehaviorSubject<IProductionMeeting[]>;
    private productionMeetings$: Observable<IProductionMeeting[]>;
    private serviceMeetings: BehaviorSubject<IServiceMeeting[]>;
    private serviceMeetings$: Observable<IServiceMeeting[]>;
    private meetingData: BehaviorSubject<IMeetingData>;
    private meetingData$: Observable<IMeetingData>;
    private serviceDataSub: Subscription;

    constructor(private httpService: HttpService, private cacheService: CacheService, private router: Router) {
        this.customerMeetings = new BehaviorSubject<ICustomerMeeting[]>(null);
        this.customerMeetings$ = this.customerMeetings.asObservable();
        this.internalMeetings = new BehaviorSubject<IInternalMeeting[]>(null);
        this.internalMeetings$ = this.internalMeetings.asObservable();
        this.operatorMeetings = new BehaviorSubject<IOperatorMeeting[]>(null);
        this.operatorMeetings$ = this.operatorMeetings.asObservable();
        this.productionMeetings = new BehaviorSubject<IProductionMeeting[]>(null);
        this.productionMeetings$ = this.productionMeetings.asObservable();
        this.serviceMeetings = new BehaviorSubject<IServiceMeeting[]>(null);
        this.serviceMeetings$ = this.serviceMeetings.asObservable();

        this.meetingData = new BehaviorSubject<IMeetingData>(null);
        this.meetingData$ = this.meetingData.asObservable();

        // Subscription for service data
        this.serviceDataSub = this.httpService.getServiceData$().subscribe(
            data => {
                this.handleServiceData(data as IAppServiceData);
            }, error => {
            }
        );
    }

    /**
     * Handles service data updates
     * @param {IAppServiceData} serviceData
     */
    public handleServiceData(serviceData: IAppServiceData = null): void {
        if (!isNull(serviceData)) {
            let bSetNewData = false;
            let updatedCustomerMeetings: ICustomerMeeting[] = this.customerMeetings.getValue();
            let updatedInternalMeetings: IInternalMeeting[] = this.internalMeetings.getValue();
            let updatedOperatorMeetings: IOperatorMeeting[] = this.operatorMeetings.getValue();
            let updatedProductionMeetings: IProductionMeeting[] = this.productionMeetings.getValue();
            let updatedServiceMeetings: IServiceMeeting[] = this.serviceMeetings.getValue();
            if (!isNull(serviceData.customerMeetings)) {
                // Update service data
                updatedCustomerMeetings = ServiceHelper.updateServiceEntries(serviceData.customerMeetings, this.customerMeetings.getValue()) as ICustomerMeeting[];
                bSetNewData = true;
            }
            if (!isNull(serviceData.internalMeetings)) {
                // Update service data
                updatedInternalMeetings = ServiceHelper.updateServiceEntries(serviceData.internalMeetings, this.internalMeetings.getValue()) as IInternalMeeting[];
                bSetNewData = true;
            }
            if (!isNull(serviceData.operatorMeetings)) {
                // Update service data
                updatedOperatorMeetings = ServiceHelper.updateServiceEntries(serviceData.operatorMeetings, this.operatorMeetings.getValue()) as IOperatorMeeting[];
                bSetNewData = true;
            }
            if (!isNull(serviceData.productionMeetings)) {
                // Update service data
                updatedProductionMeetings = ServiceHelper.updateServiceEntries(serviceData.productionMeetings, this.productionMeetings.getValue()) as IProductionMeeting[];
                bSetNewData = true;
            }
            if (!isNull(serviceData.operatorMeetings)) {
                // Update service data
                updatedServiceMeetings = ServiceHelper.updateServiceEntries(serviceData.serviceMeetings, this.serviceMeetings.getValue()) as IServiceMeeting[];
                bSetNewData = true;
            }
            if (bSetNewData) {
                const meetingData: {} = {
                    customerMeetings: updatedCustomerMeetings,
                    internalMeetings: updatedInternalMeetings,
                    operatorMeetings: updatedOperatorMeetings,
                    productionMeetings: updatedProductionMeetings,
                    serviceMeetings: updatedServiceMeetings,
                };
                this.setMeetingServiceData(meetingData as IMeetingData);
            }
        }
    }

    /**
     * Sets customer service and cache data
     * @param meetingData
     */
    private setMeetingServiceData(meetingData: IMeetingData) {
        this.meetingData.next(meetingData);
        this.customerMeetings.next(meetingData.customerMeetings);
        this.internalMeetings.next(meetingData.internalMeetings);
        this.operatorMeetings.next(meetingData.operatorMeetings);
        this.productionMeetings.next(meetingData.productionMeetings);
        this.serviceMeetings.next(meetingData.serviceMeetings);
    }

    /**
     * Reset service data (on logout)
     */
    public resetMeetingServiceData() {
        this.meetingData.next(null);
        this.customerMeetings.next(null);
        this.internalMeetings.next(null);
        this.operatorMeetings.next(null);
        this.productionMeetings.next(null);
        this.serviceMeetings.next(null);
    }

    public getMeetingsData$(): Observable<IMeetingData> {
        return this.meetingData$;
    }

    public getCustomerMeetings$(): Observable<ICustomerMeeting[]> {
        return this.customerMeetings$;
    }

    public getInternalMeetings$(): Observable<IInternalMeeting[]> {
        return this.internalMeetings$;
    }

    public getOperatorMeetings$(): Observable<IOperatorMeeting[]> {
        return this.operatorMeetings$;
    }

    public getProductionMeetings$(): Observable<IProductionMeeting[]> {
        return this.productionMeetings$;
    }

    public getServiceMeetings$(): Observable<IServiceMeeting[]> {
        return this.serviceMeetings$;
    }

    /**
     * Gets all customers
     * @returns {Observable<IApiResponse>}
     */
    public apiGetMeetingData(): void {
        this.httpService.apiGet('get-meeting-data').subscribe(
            data => {
                const res: IMeetingData = data.result_data as IMeetingData;
                this.setMeetingServiceData(res);
            },
            error => {}
        );
    }

    /**
     * Create customer meeting
     * @param {ICustomerNote} customerMeeting
     * @returns {Observable<IApiResponse>}
     */
    public apiCreateCustomerMeeting(customerMeeting: ICustomerMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('customerMeeting', JSON.stringify(customerMeeting));
        return this.httpService.apiPost('create-customer-meeting', formData);
    }

    /**
     * Edit customer meeting
     * @param {ICustomerNote} customerMeeting
     * @returns {Observable<IApiResponse>}
     */
    public apiEditCustomerMeeting(customerMeeting: ICustomerMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('customerMeeting', JSON.stringify(customerMeeting));
        return this.httpService.apiPost('edit-customer-meeting', formData);
    }

    /**
     * Delete customer meeting
     * @param {number} customerMeetingId
     * @returns {Observable<IApiResponse>}
     */
    public apiDeleteCustomerMeeting(customerMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('customerMeetingId', JSON.stringify(customerMeetingId));
        return this.httpService.apiPost('delete-customer-meeting', formData);
    }

    /**
     * Checks customer meeting report
     * @param customerMeetingId
     */
    public apiCheckCustomerMeetingReport(customerMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('customerMeetingId', JSON.stringify(customerMeetingId));
        return this.httpService.apiPost('check-customer-meeting-report', formData, true);
    }

    /**
     * Edit customer meeting report
     * @param customerMeetingId
     * @param customerMeetingReport
     */
    public apiEditCustomerMeetingReport(customerMeetingId: number, customerMeetingReport: string): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('customerMeetingId', JSON.stringify(customerMeetingId));
        formData.append('customerMeetingReport', JSON.stringify(customerMeetingReport));
        return this.httpService.apiPost('edit-customer-meeting-report', formData);
    }

    /**
     *  Create internal meeting
     * @param internalMeeting
     */
    public apiCreateInternalMeeting(internalMeeting: IInternalMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('internalMeeting', JSON.stringify(internalMeeting));
        return this.httpService.apiPost('create-internal-meeting', formData);
    }

    /**
     * Edit internal meeting
     * @param internalMeeting
     */
    public apiEditInternalMeeting(internalMeeting: IInternalMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('internalMeeting', JSON.stringify(internalMeeting));
        return this.httpService.apiPost('edit-internal-meeting', formData);
    }

    /**
     * Delete internal meeting
     * @param internalMeetingId
     */
    public apiDeleteInternalMeeting(internalMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('internalMeetingId', JSON.stringify(internalMeetingId));
        return this.httpService.apiPost('delete-internal-meeting', formData);
    }

    /**
     * Checks internal meeting report
     * @param internalMeetingId
     */
    public apiCheckInternalMeetingReport(internalMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('internalMeetingId', JSON.stringify(internalMeetingId));
        return this.httpService.apiPost('check-internal-meeting-report', formData, false);
    }

    /**
     * Edit internal meeting report
     * @param internalMeetingId
     * @param internalMeetingReport
     */
    public apiEditInternalMeetingReport(internalMeetingId: number, internalMeetingReport: string): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('internalMeetingId', JSON.stringify(internalMeetingId));
        formData.append('internalMeetingReport', JSON.stringify(internalMeetingReport));
        return this.httpService.apiPost('edit-internal-meeting-report', formData);
    }

    /**
     * Create operator meeting
     * @param operatorMeeting
     */
    public apiCreateOperatorMeeting(operatorMeeting: IOperatorMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('operatorMeeting', JSON.stringify(operatorMeeting));
        return this.httpService.apiPost('create-operator-meeting', formData);
    }

    /**
     * Edit operator meeting
     * @param operatorMeeting
     */
    public apiEditOperatorMeeting(operatorMeeting: IOperatorMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('operatorMeeting', JSON.stringify(operatorMeeting));
        return this.httpService.apiPost('edit-operator-meeting', formData);
    }

    /**
     * Delete operator meeting
     * @param {number} operatorMeetingId
     * @returns {Observable<IApiResponse>}
     */
    public apiDeleteOperatorMeeting(operatorMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('operatorMeetingId', JSON.stringify(operatorMeetingId));
        return this.httpService.apiPost('delete-operator-meeting', formData);
    }

    /**
     * Checks operator meeting report
     * @param operatorMeetingId
     */
    public apiCheckOperatorMeetingReport(operatorMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('operatorMeetingId', JSON.stringify(operatorMeetingId));
        return this.httpService.apiPost('check-operator-meeting-report', formData, true);
    }

    /**
     * Edit operator meeting report
     * @param operatorMeetingId
     * @param operatorMeetingReport
     */
    public apiEditOperatorMeetingReport(operatorMeetingId: number, operatorMeetingReport: string): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('operatorMeetingId', JSON.stringify(operatorMeetingId));
        formData.append('operatorMeetingReport', JSON.stringify(operatorMeetingReport));
        return this.httpService.apiPost('edit-operator-meeting-report', formData);
    }

    /**
     * Create production meeting
     * @param productionMeeting
     */
    public apiCreateProductionMeeting(productionMeeting: IProductionMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('productionMeeting', JSON.stringify(productionMeeting));
        return this.httpService.apiPost('create-production-meeting', formData);
    }

    /**
     * Edit production meeting
     * @param productionMeeting
     */
    public apiEditProductionMeeting(productionMeeting: IProductionMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('productionMeeting', JSON.stringify(productionMeeting));
        return this.httpService.apiPost('edit-production-meeting', formData);
    }

    /**
     * Delete production meeting
     * @param {number} productionMeetingId
     * @returns {Observable<IApiResponse>}
     */
    public apiDeleteProductionMeeting(productionMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('productionMeetingId', JSON.stringify(productionMeetingId));
        return this.httpService.apiPost('delete-production-meeting', formData);
    }

    /**
     * Checks production meeting report
     * @param productionMeetingId
     */
    public apiCheckProductionMeetingReport(productionMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('productionMeetingId', JSON.stringify(productionMeetingId));
        return this.httpService.apiPost('check-production-meeting-report', formData, true);
    }

    /**
     * Edit production meeting report
     * @param productionMeetingId
     * @param productionMeetingReport
     */
    public apiEditProductionMeetingReport(productionMeetingId: number, productionMeetingReport: string): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('productionMeetingId', JSON.stringify(productionMeetingId));
        formData.append('productionMeetingReport', JSON.stringify(productionMeetingReport));
        return this.httpService.apiPost('edit-production-meeting-report', formData);
    }

    /**
     * Create service meeting
     * @param serviceMeeting
     */
    public apiCreateServiceMeeting(serviceMeeting: IServiceMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('serviceMeeting', JSON.stringify(serviceMeeting));
        return this.httpService.apiPost('create-service-meeting', formData);
    }

    /**
     * Edit service meeting
     * @param serviceMeeting
     */
    public apiEditServiceMeeting(serviceMeeting: IServiceMeeting): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('serviceMeeting', JSON.stringify(serviceMeeting));
        return this.httpService.apiPost('edit-service-meeting', formData);
    }

    /**
     * Delete service meeting
     * @param {number} serviceMeetingId
     * @returns {Observable<IApiResponse>}
     */
    public apiDeleteServiceMeeting(serviceMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('serviceMeetingId', JSON.stringify(serviceMeetingId));
        return this.httpService.apiPost('delete-service-meeting', formData);
    }

    /**
     * Checks production meeting report
     * @param serviceMeetingId
     */
    public apiCheckServiceMeetingReport(serviceMeetingId: number): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('serviceMeetingId', JSON.stringify(serviceMeetingId));
        return this.httpService.apiPost('check-service-meeting-report', formData, true);
    }

    /**
     * Edit service meeting report
     * @param serviceMeetingId
     * @param serviceMeetingReport
     */
    public apiEditServiceMeetingReport(serviceMeetingId: number, serviceMeetingReport: string): Observable<IApiResponse> {
        const formData = new FormData();
        formData.append('serviceMeetingId', JSON.stringify(serviceMeetingId));
        formData.append('serviceMeetingReport', JSON.stringify(serviceMeetingReport));
        return this.httpService.apiPost('edit-service-meeting-report', formData);
    }

    /**
     * Returns meeting list as excel
     * @param meetingList
     * @param fileName
     */
    public apiExportMeetingList(meetingList: {}[], fileName: string): void {
        const formData = new FormData();
        formData.append('meetingList', JSON.stringify(meetingList));
        this.httpService.apiFilePost('export-meeting-list', formData).subscribe(
            data => {
                saveAs(new Blob([data], {type: 'xlsx'}), fileName + '.xlsx');
            },
            error => {
            }
        );
    }
}
