'use strict';

import {Observable} from 'rxjs';
import {isFunction, isUndefined} from 'util';
import {IQuery, IQueryObject, IQueryWhere} from 'blow-query';
import {BaseModel} from './BaseModel';
import * as helpers from './helpers';
import {
  IPersistedAdapter, 
  IPersistedModel, 
  IPersistedModelConstructor,
  IBaseModelData, 
  IBaseModelConstructor
} from './interfaces';
 


export class PersistedModel extends BaseModel implements IPersistedModel { 

  destroy(): Observable<boolean> {
    return (<IPersistedModelConstructor>helpers.getCtor(this))
      .destroyById(helpers.getIdValue(this));
  }
  
  save(): Observable<IPersistedModel> {
    return (<IPersistedModelConstructor>helpers.getCtor(this))
      .updateOrCreate(this).map(saved => this.merge(saved.toJSON ? saved.toJSON() : saved));
  }
  
  refresh(): Observable<IPersistedModel> {
    return (<IPersistedModelConstructor>helpers.getCtor(this))
      .findById(helpers.getIdValue(this))
      .map(refreshed => this.merge(refreshed.toJSON ? refreshed.toJSON() : refreshed))
  }
  
  protected static _prepareSave(data: IBaseModelData | PersistedModel): IBaseModelData {
    if(data instanceof PersistedModel) {
      return data.toJSON();
    }
    return data;
  }
  
  static init(data: IBaseModelData): PersistedModel {
    return new this(data);
  }
  
  static count(where?: IQueryWhere): Observable<number> {
    return this.adapter.count(this.metadata, where);
  }
  
  static create(data: IBaseModelData | IPersistedModel): Observable<IPersistedModel> {
    return this.adapter.create(this.metadata, this._prepareSave(data))
      .map(result => this.init(result));
  }
  
  static destroy(where?: IQueryWhere): Observable<number> {
    return this.adapter.destroy(this.metadata, where);    
  }
  
  static destroyById(id: any): Observable<boolean> {
    return this.adapter.destroyById(this.metadata, id);
  }
  
  static exists(id: any): Observable<boolean> {
    return this.adapter.exists(this.metadata, id);
  }

  static find(query?: IQuery | IQueryObject): Observable<IPersistedModel> {
    return this.adapter.find(this.metadata, query)
      .map(result => this.init(result));
  }
  
  static findOne(query?: IQuery | IQueryObject): Observable<IPersistedModel> {
    return this.adapter.findOne(this.metadata, query)
      .map(result => this.init(result));
  }
  
  static findById(id: any): Observable<IPersistedModel> {
    return this.adapter.findById(this.metadata, id)
      .map(result => this.init(result));
  }
  
  static findOrCreate(where: IQueryWhere, data: IBaseModelData | IPersistedModel): Observable<IPersistedModel> {
    return this.adapter.findOrCreate(this.metadata, where, this._prepareSave(data))
      .map(result => this.init(result));
  }
  
  static update(where: IQueryWhere, data: IBaseModelData | IPersistedModel): Observable<number> {
    return this.adapter.update(this.metadata, where, this._prepareSave(data));
  }
  
  static updateOrCreate(data: IBaseModelData | IPersistedModel): Observable<IPersistedModel> {
    return this.adapter.updateOrCreate(this.metadata, this._prepareSave(data))
      .map(result => this.init(result));
  }
  
  static get adapter(): IPersistedAdapter {
    return <IPersistedAdapter>this.connection.adapter;
  }
 
}