import { Injectable, EmbeddedViewRef, ViewRef } from '@angular/core';
import { Headers, Http, RequestOptions, Response } from '@angular/http';

import { Observable } from 'rxjs/Observable';

import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';

import {
  IDiscoverKnoks, IKnok, IKnokFeedArgs, IKnokFeedDataList, IKnokOngoingDataList, IKnokEndedDataList, IKnokRate, IKnokserProfile,
  IPurchaseKnok, IReference, IReferenceArg, IKnokOngoingArgs, IKnokFullData, IKnokStartvalues, IOngoingKnokFeedback, IEndedKnokFeedback
} from './knok.model';

import { TokenService } from '../token/token.service';

import { IApiError, IApiResponseError, IApiResponseSuccess, isApiResponseError } from '../token/apiresponse.model';

import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { CExchanges, IExchanges } from '../exchange/exchange.model';
import { Params } from '@angular/router';
import { URLSearchParams } from '@angular/http/src/url_search_params';
import { Subject } from 'rxjs/Subject';
import { convertUTCDateToLocalDate } from '../helpers';
import { IUser } from '../user/user.model';

@Injectable()
export class KnokService {
  knok: IKnok;
  private subjectSetPanel: Subject<any> = new Subject<any>();
  private subjectUnsetPanel: Subject<any> = new Subject<any>();

  constructor(private http: Http, private tokenService: TokenService) { }

  get setPanelWatcher(): Observable<ViewRef> {
    return this.subjectSetPanel.asObservable();
  }

  get unsetPanelWatcher(): Observable<ViewRef> {
    return this.subjectUnsetPanel.asObservable();
  }

  setPanel(viewRef: ViewRef): void {
    this.subjectSetPanel.next(viewRef);
  }

  unsetPanel(): void {
    this.subjectUnsetPanel.next();
  }

    createKnok(knok: IKnok): Observable<IKnok> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/createknok`, knok, options);
        return req.map((response: Response) => {
            const data: IApiResponseError | IApiResponseSuccess<number> = response.json();
            if (isApiResponseError(data)) {
                throw Observable.throw(data.error);
            }
            knok.knokId = data.result;
            this.knok = knok;
            return knok;
        }).catch((error: IApiError): Observable<IKnok> => {
            console.error(error);
            return Observable.throw(error.msg || error);
        });
    }

    validateKnock(knok: IKnok): Observable<IApiError> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/validateknok`, knok, options);
        return req.map((response: Response) => {
            const data: IApiResponseError | IApiResponseSuccess<number> = response.json();
            if (isApiResponseError(data)) {
                return (data.error);
            }
            return null;
        }).catch((error) => {
            console.error(error);
            return Observable.throw(error.msg || error);
        });
    }

    rateKnok(knok: IKnok): Observable<IKnokRate> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/rateknok`, knok, options);
        return req.map((response: Response) => {
            const data: IApiResponseError | IApiResponseSuccess<IKnokRate> = response.json();
            if (isApiResponseError(data)) {
                return (data.error);
            }
            if ('value' in data.result) {
              return data.result['value'];
            }
            return data.result;
        }).catch((error) => {
            console.error(error);
            return Observable.throw(error.msg || error);
        });
    }

    getDiscoverKnoks(args: IKnokFeedArgs): Observable<IKnokFeedDataList> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/feed`, args, options);
        return req.map((response: Response) => {
                const data: IApiResponseError | IApiResponseSuccess<IKnokFeedDataList> = response.json();
                if (isApiResponseError(data)) {
                    return Observable.throw(data.error);
                }
                data.result.items.forEach(item => {
                    item.knok.createDate = convertUTCDateToLocalDate(new Date(item.knok.createDate));
                });
                return data.result;
            })
            .map((feeds: IKnokFeedDataList) => {
                return feeds;
            }).catch((error) => { // .catch((error: IApiError): Observable<boolean> => {
                console.error(error);
                return Observable.throw(error.msg || error);
            });
                //         console.log(error);
                //         return Observable.throw(error.msg || error);
                //     });
            // return Observable.of({items: [
            // {
            //     knokId: null, userName: 'Ralph Stephens',   avatarUrl: '',    followers: 815,    raiting: 4, priceUSD: 40, priceTokens: 0.51, value: 38.50, timeLeft: new Date('2010-08-09 22:15:00'), purchases: 620,
            //     currencyPair: 'EOS / ETH',    exchangeName: 'Binance', potentialProfit: 1,    hasTechnical: true,    hasFundamental: true, isEntryPriceTouched: true
            // },
            // {
            //     knokId: null, userName: 'Chester Arnold',    avatarUrl: 'http://i.pravatar.cc/300',
            //     followers: 641,    raiting: 5, priceUSD: 40, priceTokens: 0.51, value: 40.03, timeLeft: new Date('2010-08-09 18:42:00'), purchases: 728,
            //     currencyPair: 'LTC / ETH',    exchangeName: 'Coinbase',  hasTechnical: true,    hasFundamental: false, isEntryPriceTouched: false
            // }
            // ]})
            // .map((knoks: IDiscoverKnoks) => {
            //     if (knoks.items.length > 0) {
            //         this.discoverKnoks = knoks;
            //         return knoks;
            //     }
            // }).
        // ).catch((error: IApiError): Observable<IDiscoverKnoks> => {
        //         console.log(error);
        //         return Observable.throw(error.msg || error);
        //     });
    }

    getOngoingKnoks(args: IKnokOngoingArgs): Observable<IKnokOngoingDataList> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/track`, args, options);
        return req.map((response: Response) => {
                const data: IApiResponseError | IApiResponseSuccess<IKnokOngoingDataList> = response.json();
                if (isApiResponseError(data)) {
                    return Observable.throw(data.error);
                }
                // Debug
                data.result.items.forEach(item => {
                    item.knok.createDate = convertUTCDateToLocalDate(new Date(item.knok.createDate));
                    item.chartData.forEach(item1 => {
                        item1.x = convertUTCDateToLocalDate(new Date(item1.x));
                    });
                    // let minDuration: number = 1;
                    // let maxDuration: number = 27;
                    // item.knok.duration = Math.round((Math.random() * (maxDuration - minDuration)) + minDuration);

                    // let minEntryFrom: number = 0.09;
                    // let maxEntryFrom: number = 0.21;
                    // item.knok.entryPriceFrom = Math.round(((Math.random() * (maxEntryFrom - minEntryFrom)) + minEntryFrom) * 100) / 100; // 0.1;

                    // let minEntryTo: number = item.knok.entryPriceFrom + 0.01;
                    // let maxEntryTo: number = item.knok.entryPriceFrom + item.knok.entryPriceFrom * 0.2; // 20%
                    // item.knok.entryPriceTo = Math.round(((Math.random() * (maxEntryTo - minEntryTo)) + minEntryTo) * 100) / 100; // 0.12;

                    // let minStopLoss: number = 0.01;
                    // let maxStopLoss: number = item.knok.entryPriceFrom - item.knok.entryPriceFrom * 0.2;
                    // item.knok.stopLoss = Math.round(((Math.random() * (maxStopLoss - minStopLoss)) + minStopLoss) * 100) / 100; // 0.05

                    // let minExitFrom: number = item.knok.entryPriceTo + item.knok.entryPriceTo * 0.05;
                    // let maxExitFrom: number = minExitFrom + 0.05;
                    // item.knok.exitPriceFrom = Math.round(((Math.random() * (maxExitFrom - minExitFrom)) + minExitFrom) * 100) / 100; // 0.2;

                    // let minExitTo: number = item.knok.entryPriceFrom + 0.1;
                    // let maxExitTo: number = minExitTo + 0.05;
                    // item.knok.exitPriceTo = Math.round(((Math.random() * (maxExitTo - minExitTo)) + minExitTo) * 100) / 100; // 0.22;

                    // if (item.knok.entryPriceTo - item.knok.entryPriceFrom < 0.05) {
                    //     item.knok.entryPriceFrom = item.knok.entryPriceFrom - 0.05;
                    // }
                    // if (item.knok.exitPriceTo - item.knok.exitPriceFrom < 0.05) {
                    //     item.knok.exitPriceTo = item.knok.exitPriceTo + 0.05;
                    // }
                    // if (item.knok.entryPriceFrom - item.knok.stopLoss < 0.05) {
                    //     item.knok.stopLoss = item.knok.stopLoss - 0.05;
                    // }
                    // if (item.knok.stopLoss <= 0) {
                    //     const d: number = -item.knok.stopLoss + 0.05;
                    //     item.knok.stopLoss = item.knok.stopLoss + d;
                    //     item.knok.entryPriceFrom = item.knok.entryPriceFrom + d;
                    //     item.knok.entryPriceTo = item.knok.entryPriceTo + d;
                    //     item.knok.exitPriceFrom = item.knok.exitPriceFrom + d;
                    //     item.knok.exitPriceTo = item.knok.exitPriceTo + d;
                    // }
                    // item.chartData = [];

                    // const oneDay: number = (24 * 60 * 60 * 1000);
                    // let currDate: Date = new Date();
                    // currDate = new Date(`${currDate.getMonth() + 1}/${currDate.getDate()}/${currDate.getFullYear()}`);
                    // const daysLeft: number = Math.round(Math.random() * (item.knok.duration - 1) + 1);
                    // item.knok.createDate = new Date(currDate.getTime() - ((item.knok.duration - daysLeft) * oneDay));
                    // item.knok.createDate = new Date(`${item.knok.createDate.getMonth() + 1}/${item.knok.createDate.getDate()}/${item.knok.createDate.getFullYear()}`);
                    // const endDate: Date = new Date(item.knok.createDate.getTime() + item.knok.duration * oneDay);
                    // const elems: number = item.knok.duration - daysLeft;
                    // // const i: number = (endDate.getTime() - item.knok.createDate.getTime()) / elems;
                    // let date: Date = item.knok.createDate;
                    // while (date.getTime() <= endDate.getTime()) {
                    //     let nextDate: Date = new Date(date.getTime() + oneDay * 2);

                    //     if (currDate.getDate() === date.getDate() &&
                    //         currDate.getMonth() === date.getMonth() &&
                    //         currDate.getFullYear() === date.getFullYear() &&

                    //         date.getDate() !== nextDate.getDate()) {
                    //             item.chartData.push({
                    //                 x: date,
                    //                 y: Math.round((Math.random() * (item.knok.exitPriceTo - item.knok.stopLoss) + item.knok.stopLoss) * 100) / 100
                    //             });
                    //     } else {
                    //         if (currDate.getTime() > date.getTime()) {
                    //             item.chartData.push({
                    //             x: date,
                    //             y: Math.round((Math.random() * (item.knok.exitPriceTo - item.knok.stopLoss) + item.knok.stopLoss) * 100) / 100
                    //             });
                    //         } else {
                    //             if (endDate.getTime() === date.getTime()) {
                    //                 item.chartData.push({
                    //                 x: date,
                    //                 y: null
                    //             });
                    //             }
                    //         }
                    //     }
                    //     date = new Date(date.getTime() + (24 * 60 * 60 * 1000));
                    // }
                });
                // Debug
                return data.result;
            })
            .map((ongoings: IKnokOngoingDataList) => {
                return ongoings;
            }).catch((error) => {
                console.error(error);
                return Observable.throw(error.msg || error);
            });
    }

    getEndedKnoks(args: IKnokOngoingArgs): Observable<IKnokEndedDataList> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/ended`, args, options);
        return req.map((response: Response) => {
                const data: IApiResponseError | IApiResponseSuccess<IKnokEndedDataList> = response.json();
                if (isApiResponseError(data)) {
                    return Observable.throw(data.error);
                }
                data.result.items.forEach(item => {
                    item.knok.createDate = convertUTCDateToLocalDate(new Date(item.knok.createDate));
                    item.knok.closeDate = convertUTCDateToLocalDate(new Date(item.knok.closeDate));
                    item.chartData.forEach(item1 => {
                        item1.x = convertUTCDateToLocalDate(new Date(item1.x));
                    });
                });
                return data.result;
            })
            .map((ongoings: IKnokEndedDataList) => {
                return ongoings;
            }).catch((error) => {
                console.error(error);
                return Observable.throw(error.msg || error);
            });
    }

  getCurrentlyPublishedKnoks(args: IKnokOngoingArgs): Observable<IKnokOngoingDataList> {
    const headers: Headers = new Headers({
      'Content-Type': 'application/json',
      Authorization: this.tokenService.authorization
    });
    const options: RequestOptions = new RequestOptions({headers});
    const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/currentlypublished`, args, options);
    return req.map((response: Response) => {
      const data: IApiResponseError | IApiResponseSuccess<IKnokOngoingDataList> = response.json();
      if (isApiResponseError(data)) {
        return Observable.throw(data.error);
      }
      data.result.items.forEach(item => {
        item.knok.createDate = convertUTCDateToLocalDate(new Date(item.knok.createDate));
        item.chartData.forEach(item1 => {
          item1.x = convertUTCDateToLocalDate(new Date(item1.x));
        });
      });
      return data.result;
    }).map((ongoings: IKnokOngoingDataList) => {
      return ongoings;
    }).catch((error) => {
      // console.error(error);
      return Observable.throw(error.msg || error);
    });
  }

  getPastPublishedKnoks(args: IKnokOngoingArgs): Observable<IKnokEndedDataList> {
    const headers: Headers = new Headers({
      'Content-Type': 'application/json',
      Authorization: this.tokenService.authorization
    });
    const options: RequestOptions = new RequestOptions({headers});
    const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/pastpublished`, args, options);
    return req.map((response: Response) => {
      const data: IApiResponseError | IApiResponseSuccess<IKnokEndedDataList> = response.json();
      if (isApiResponseError(data)) {
        return Observable.throw(data.error);
      }
      data.result.items.forEach(item => {
        item.knok.createDate = convertUTCDateToLocalDate(new Date(item.knok.createDate));
        item.knok.closeDate = convertUTCDateToLocalDate(new Date(item.knok.closeDate));
        item.chartData.forEach(item1 => {
          item1.x = convertUTCDateToLocalDate(new Date(item1.x));
        });
      });
      return data.result;
    }).map((ongoings: IKnokEndedDataList) => {
      return ongoings;
    }).catch((error) => {
      // console.error(error);
      return Observable.throw(error.msg || error);
    });
  }

    getKnokFAReference(args: IReferenceArg): Observable<IReference | IApiError> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/general/parseUrl`, args, options);
        return req.map((response: Response) => {
                const data: IApiResponseError | IApiResponseSuccess<IReference> = response.json();
                if (isApiResponseError(data)) {
                    return data.error;
                }
                return data.result;
            })
            .map((ref: IReference) => {
                return ref;
            }).catch((error) => { // .catch((error: IApiError): Observable<boolean> => {
                console.error(error);
                return Observable.throw(error.msg || error);
            });
    }

    purchaseKnok(knokId: number): Observable<IPurchaseKnok> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/knok/purchase/${knokId}`, {}, options);
        return req.map((response: Response) => {
            const data: IApiResponseError | IApiResponseSuccess<IPurchaseKnok> = response.json();
            if (isApiResponseError(data)) {
                throw Observable.throw(data.error);
            }
            return data.result;
        }).catch((error: IApiError): Observable<IPurchaseKnok> => {
            console.error(error);
            return Observable.throw(error.msg || error);
        });
    }

    getStatus(valueFrom: number, valueTo: number, MAR: number): string {
        // (Entry price range/MAR)*100
        const val: number =  (1 - (valueTo - valueFrom) / (MAR / 100)) * 100; // Math.round((valueTo - valueFrom) / (valueFrom / 100 * MAR) * 100);
        // const val: number = (valueTo - valueFrom) / (valueFrom * MAR) * 1000;
        switch (true) {
        case val <= 100 && val > 80 : // 100 - 80 = week
            return 'weak';
        case val <= 80 && val > 40 : // 80 - 40 = medium
            return 'medium';
        case val <= 40 && val >= 0 : // 40 - 0 = strong
            return 'strong';
        default:
            return 'weak';
        }
    }

    getKnokData(knokId: number, userId?: number | null | undefined): Observable<IKnokFullData> {
      const headers: Headers = new Headers({
        'Content-Type': 'application/json',
        Authorization: this.tokenService.authorization
      });
      const params: string | URLSearchParams | {
        [key: string]: any | any[];
      } | null = {userId: userId || null};
      const options: RequestOptions = new RequestOptions({headers, params});
      const request: Observable<Response> = this.http.get(`${SETTINGS.API_URL}/knok/GetKnok/${knokId}`, options);
      return request.map((response: Response) => {
        const data: IApiResponseError | IApiResponseSuccess<IKnokFullData> = response.json();
        if (isApiResponseError(data)) {
          return Observable.throw(data.error);
        }
        // todo: verify of result
        data.result.knok.createDate = convertUTCDateToLocalDate(new Date(data.result.knok.createDate));
        data.result.knok.buyDate = new Date(data.result.knok.buyDate);
        return data.result;
      }).catch(this.handleError);
    }

    getKnokStartvalues(exchangeId: number = undefined, currency1: string = null, currency2: string = null): Observable<IKnokStartvalues | IApiError> {
      const headers: Headers = new Headers({
        'Content-Type': 'application/json',
        Authorization: this.tokenService.authorization
      });
      const params: string | URLSearchParams | {
        [key: string]: any | any[];
      } | null = {exchangeId, currency1, currency2};
      const options: RequestOptions = new RequestOptions({headers, params});
      const request: Observable<Response> = this.http.get(`${SETTINGS.API_URL}/general/KnokStartvalues`, options);
      return request.map((response: Response) => {
        const data: IApiResponseError | IApiResponseSuccess<any> = response.json();
        if (isApiResponseError(data)) {
          return Observable.throw(data.error);
        }
        return data.result.value;
      }).catch(this.handleError);
    }

    knokserProfile(knokserId: number): Observable<IKnokserProfile> {
        const headers: Headers = new Headers({
            'Content-Type': 'application/json',
            Authorization: this.tokenService.authorization
        });
        const options: RequestOptions = new RequestOptions({ headers });
        const req: Observable<Response> = this.http.get(`${SETTINGS.API_URL}/knok/knokser/${knokserId}`,  options);
        return req.map((response: Response) => {
            const data: IApiResponseError | IApiResponseSuccess<IKnokserProfile> = response.json();
            if (isApiResponseError(data)) {
                return (data.error);
            }
            return data.result;
        }).catch((error) => {
            console.error(error);
            return Observable.throw(error.msg || error);
        });
    }

  addOngoingKnokFeedback(data: IOngoingKnokFeedback): Observable<any> {
    const headers: Headers = new Headers({
      'Content-Type': 'application/json',
      Authorization: this.tokenService.authorization
    });
    const options: RequestOptions = new RequestOptions({headers});
    const response: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/general/AddOngoingKnokFeedback`, data, options);
    return response.map((response: Response) => {
      let data: IApiResponseError | IApiResponseSuccess<IUser> = response.json();
      if (isApiResponseError(data)) {
        throw Observable.throw(data.error);
      }
      return data.result;
    }).catch((error) => {
      // console.error(error);
      return Observable.throw(error.msg || error);
    });
  }

  addEndedKnokFeedback(data: IEndedKnokFeedback): Observable<any> {
    const headers: Headers = new Headers({
      'Content-Type': 'application/json',
      Authorization: this.tokenService.authorization
    });
    const options: RequestOptions = new RequestOptions({headers});
    const response: Observable<Response> = this.http.post(`${SETTINGS.API_URL}/general/AddEndedKnokFeedback`, data, options);
    return response.map((response: Response) => {
      let data: IApiResponseError | IApiResponseSuccess<IUser> = response.json();
      if (isApiResponseError(data)) {
        throw Observable.throw(data.error);
      }
      return data.result;
    }).catch((error) => {
      // console.error(error);
      return Observable.throw(error.msg || error);
    });
  }

  private handleError(error: IApiError): Observable<any> {
    return Observable.throw(error.msg || error);
  }
}
