import { Injectable } from '@angular/core';
import { Platform } from 'ionic-angular';
import PouchDB from 'pouchdb';
import * as PouchRelational from 'relational-pouch';
import * as PouchUpsert from 'pouchdb-upsert';
import * as PouchSqlite from 'pouchdb-adapter-cordova-sqlite';
import each from 'async/each';

PouchDB.plugin(PouchRelational).plugin(PouchUpsert).plugin(PouchSqlite);

@Injectable()
export class DataProvider {

    public static readonly DOCS = {
        PARTNER: 'partner',
        USER: 'user',
        SALE_ORDER: 'sale.order',
        SALE_ORDER_LINE: 'sale.order.line',
        PRODUCT_TEMPLATE: 'product.template',
        PRODUCT_ATTRIBUTE: 'product.attribute',
        PRODUCT_ATTRIBUTE_LINE: 'product.attribute.line',
        PRODUCT_ATTRIBUTE_VALUE: 'product.attribute.value',
        PRODUCT_PRODUCT: 'product.product',
        PRODUCT_PRICELIST: 'product.pricelist',
        STOCK_WAREHOUSE: 'stock.warehouse'
    };

    db: any;
    schema: Array<any> = [
        {
            singular: 'partner',
            plural: 'partners'
        },
        {
            singular: 'user',
            plural: 'users'
        },
        {
            singular: 'sale.order',
            plural: 'sale.orders'
        },
        {
            singular: 'sale.order.line',
            plural: 'sale.order.lines'
        },
        {
            singular: 'currency',
            plural: 'currencies'
        },
        {
            singular: 'company',
            plural: 'companies'
        },
        {
            singular: 'product.template',
            plural: 'product.templates'
        },
        {
            singular: 'warehouse',
            plural: 'warehouses',
        },
        {
            singular: 'stock',
            plural: 'stocks'
        },
        {
            singular: 'lead',
            plural: 'leads'
        },
        {
            singular: 'product.attribute',
            plural: 'product.attributes'
        },
        {
            singular: 'product.attribute.line',
            plural: 'product.attribute.lines'
        },
        {
            singular: 'product.attribute.value',
            plural: 'product.attribute.values'
        },
        {
            singular: 'product',
            plural: 'products'
        },
        {
            singular: 'product.pricelist',
            plural: 'product.pricelist'
        },
        {
            singular: 'stock.warehouse',
            plural: 'stock.warehouses'
        }
    ];

    constructor(
        public platform: Platform
    ) {
        this.initialize();
    }

    /**
     *
     */
    private initialize(): void {
        this.db = new PouchDB('odoo', {
            auto_compaction: true,
            adapter: this.platform.is('core') ? 'idb' : 'websql'
        });
        
        this.db.setSchema(this.schema);
    }

    /**
     *
     */
    private getPlural(singular: string) {
        for (let i = 0; i < this.schema.length; i++) {
            if (this.schema[i].singular === singular) {
                return this.schema[i].plural;
            }
            
        }

        return null;
    }

    /**
     * Save object or array of objects to database
     * @argument type: string
     * @argument data: object or array of objects
     */
    save(type: string, data: any): Promise<any> {
        return new Promise((resolve, reject) => {
            if (data instanceof Array) {

                each(data, (i, c) => {
                    this.save(type, i).then(() => {
                        c();
                    }).catch(e => {
                        c(e);
                    });
                }, e => {
                    if (e) {
                        reject(e);
                    } else {
                        resolve();
                    }
                });

            } else {

                this.db.rel.save(type, data).then(doc => { 
                    resolve(doc[this.getPlural(type)][0]);
                }).catch(e => {
                    this.db.upsert(data.id, doc => {
                        return doc;
                    }).then(r => { 
                        resolve(r);
                    }).catch(e => {
                        reject(e);
                    });
                });   
            }
        });
    }

    /**
     *
     */
    delete(type: string, data: any): any {
        return new Promise((resolve, reject) => {
            this.platform.ready().then(r => {
                resolve(data.remote_id ? this.save(type, data) : this.db.rel.del(type, data));
            });    
        });
    }

    /**
     *
     */
    deleteAll(type: string): Promise<any> {
        return new Promise((resolve, reject) => { 
            this.getAll(type).then(data => {
                each(data, (i, c) => { 
                    this.db.rel.del(type, i).then(r => {
                        c();
                    }).catch(e => {
                        c(e);
                    });
                }, e => { 
                    if (e) {
                        reject(e);
                    } else {
                        resolve();
                    }
                });
            }).catch(e => { 
                reject(e);    
            });
        });    
    }

    /**
     *
     */
    getAll(type: string): Promise<any> {
        return new Promise((resolve, reject) => {
            this.db.rel.find(type).then(result => {
                resolve(result[this.getPlural(type)]);
            }).catch(e => {
                reject(e);
            });
        });
    }

    /**
     *
     */
    get(type: string, id?: string) {
        return new Promise((resolve, reject) => { 
            this.db.rel.find(type, id).then(result => {
                resolve(result[this.getPlural(type)]);
            }).catch(e => {
                reject(e);
            });
        });
    }

    /**
     *
     */
    dropDatabase(): any {
        return new Promise((resolve, reject) => {
            this.db.destroy().then(r => {
                this.initialize();
                resolve(r);
            }).catch(e => {
                reject(e);
            });
        });
    }

    /**
     *
     */
    parseDocID(id: string): any {
        return this.db.rel.parseDocID(id);
    }
}
