import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";

import { PouchService } from "./pouch-service";
import { OdooService } from "../services/odoo-service";

import { DocStatus } from "../enums/doc-status";

import { getMetadataStorage } from "../odoo/utils/metadata-storage";

import "rxjs/add/observable/from";
import "rxjs/add/operator/filter";
import "rxjs/add/operator/concatMap";
import "rxjs/add/operator/concat";
import "rxjs/add/operator/map";
import "rxjs/add/operator/groupBy";
import "rxjs/add/operator/mergeAll";

@Injectable()
export class SyncService {

    constructor(
        public pouch: PouchService,
        public odoo: OdooService
    ) { }

    /**
     *
     */
    do(): Observable<any> {
        return this.login().concat(this.removeAll()).concat(this.download());
    }

    /**
     *
     */
    protected login(): Observable<any> {
        let loginInformation = JSON.parse(localStorage.getItem("odoo"));

        this.odoo.initialize(loginInformation.host);
        return this.odoo.login(loginInformation.database, loginInformation.username, loginInformation.password);
    }

    /**
     *
     */
    protected logout(): Observable<any> {
        return this.odoo.logout();
    }

    /**
     *
     */
    protected getModels(): Observable<any> {
        return Observable.from(getMetadataStorage().getModels());
    }

    /**
     *
     */
    protected removeAll(): Observable<any> {
        return this.getModels().concatMap(item => this.pouch.removeAll(item.model.document));
    }

    /**
     *
     */
    protected download(): Observable<any> {
        return this.getModels()
            .concatMap(item => this.odoo.searchRead(item.model.name, []).map(r => {
                return Object.assign(r, { type: item.model.document });
            }))
            .concatMap(data => this.store(data));
    }

    /**
     *
     */
    protected store(data: any): Observable<any> {
        let type = data.type;
        let records = data.records;
        
        return Observable.from(records).map((r: any) => {
            r.remote_id = r.id;
            delete r.id;
            
            return r;
        }).concatMap(item => this.pouch.save(Object.assign(item, {
            type: type,
            doc_status: DocStatus.None
        })));
    }

    /**
     *
     */
    protected upload(): Observable<any> {
        return this.getModels()
            .concatMap(item => this.pouch.getAll(item.model.document))
            .concatMap(result => Observable.from(result.docs).filter((r: any) => {
                return r.doc_status != DocStatus.None;
            }).groupBy((r: any) => {
                return r.doc_status;
            }))
            .mergeAll()
            .concatMap(doc => this.send(doc));
    }

    /**
     *
     */
    protected send(data: any): Observable<any> {
        let id: number = data.remote_id;
        let model: string = data.type;

        delete data.type;
        delete data.remote_id;

        if (data.doc_state === DocStatus.Created) {
            return this.odoo.create(model, data);
        }
        
        if (data.doc_state === DocStatus.Updated) {
            return this.odoo.write(model, [[id], data]);
        } 

        return this.odoo.unlink(model, [[id]]);
    }
}