import {Injectable} from "@angular/core";
import {
  HTTP_INTERCEPTORS,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
  HttpResponse
} from '@angular/common/http';
import {Observable, of, throwError} from 'rxjs';
import {delay, dematerialize, materialize, mergeMap} from 'rxjs/operators';
import {DayToDisplay, Event, EventType, Trainer} from "../services/event.service";
import * as moment from "moment";


/*
FAKE BACKEND

This will serve as a standalone backend with delayed response so that it can imitate a real backend - using
DELAYED OBSERVABLE

1. It will check the user credentails that come from "Authentication Service" during login
2. It will also work as a fake database keeping the user details - The user can requests for the user details only when the requests has valid JWT Token in its request authorization header

API
1. To check credentials - /users/authenticate
2. To give back user details - /users
*/


@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {

  constructor() {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // test user - (one of the users detais in database)
    let testUser = {
      id: 1,
      username: 'test',
      password: 'test',
      firstName: 'Test',
      lastName: 'User'
    };

    let eventTypes: EventType[] = [
      {
        id: 1,
        name: 'Típus 1',
        theme: '0'
      },
      {
        id: 2,
        name: 'Típus 2',
        theme: '0'
      }
    ];

    let trainer1: Trainer = {
      id: 1,
      name: "Fitness Trainer"
    };

    let trainer2: Trainer = {
      id: 1,
      name: "weight lift Trainer"
    };

    let events: Event[] = [];

    let id = 1000;
    for (let dayIndex = 0; dayIndex < 28; dayIndex++) {
      for (let hourIndex = 8; hourIndex < 22; hourIndex++) {
        for (let itemIndex = 0; itemIndex < 3; itemIndex++) {

          id = id + 1;
          let reservedAt = id % 2 ? this.createEventDate(0, 1) : null;
          events.push(
            {
              room: undefined,
              id: id,
              name: 'esemény ' + id,
              start: this.createEventDate(dayIndex, hourIndex),
              end: this.createEventDate(dayIndex, hourIndex + 1),
              trainer: [trainer1, trainer2][id % 2],
              eventType: eventTypes[id % eventTypes.length],
              // reservedAt: reservedAt,
              registrations: [],
              reservationCount: id % 11,
              seat_count: 10
            }
          )
        }
      }
    }


    // wrapping the API's in delayed observable to simulate the Server API Calls
    return of(null).pipe(
      mergeMap(() => {

        // API 1: Authenticate - Will be hit by Authentication Service - STARTS
        if (request.url.endsWith('/users/authenticate') && request.method === 'POST') {


          //check the credentials entered by the user with the data in database
          if (request.body.username === testUser.username && request.body.password === testUser.password) {

            // if login details are valid, return status 200 with a Fake JWT Token
            let body = {
              id: testUser.id,
              username: testUser.username,
              firstName: testUser.firstName,
              lastName: testUser.lastName,
              token: '0000-fake-jwt-token-0000'
            };

            return of(new HttpResponse({status: 200, body}))
          } else {
            // if the credentials by user doesn't match the data in the db, return Status 400 - Bad Request
            return throwError({
              error: {
                message: 'Username or Password is incorrect.'
              }
            })
          }
        }
        // API 1: Authenticate - Will be hit by Authentication Service - ENDS


        // SECURE API END POINT - will check for valid JWT Token in Request
        // API 2: Get all users data (we now have only 1 user - testUser) - STARTS
        if (request.url.endsWith('/users') && request.method === 'GET') {

          // check for a fake jwt token. If valid JWT token found, return the list of users, else throw error
          if (request.headers.get('Authorization') === 'Bearer 0000-fake-jwt-token-0000') {
            return of(new HttpResponse({status: 200, body: [testUser]}));
          } else {
            // invalid JWT token found in request header
            return throwError({
              error: {
                message: 'Unauthorized'
              }
            });
          }
        }
        // API 2: Get all users data (we now have only 1 user - testUser) - ENDS


        if (request.url.endsWith('/event-type') && request.method === 'GET') {

          // check for a fake jwt token. If valid JWT token found, return the list of users, else throw error
          if (request.headers.get('Authorization') === 'Bearer 0000-fake-jwt-token-0000') {
            return of(new HttpResponse({status: 200, body: eventTypes}));
          } else {
            // invalid JWT token found in request header
            return throwError({
              error: {
                message: 'Unauthorized'
              }
            });
          }
        }
        if (request.url.endsWith('/events/available') && request.method === 'GET') {

          const data = {
            days: [],
            events: []
          };
          moment.locale("hu");
          let _moment = moment();
          const today = _moment.startOf('day');

          const startOfWeek = _moment.clone().startOf("week").startOf('day');

          const nextDays :DayToDisplay[] = [];
          for (let i = 0; i < 21; i++) {
            let active = false;
            let nextDay = startOfWeek.clone().add(i, 'd').startOf("day");
            if (nextDay.isAfter(today) || nextDay.isSameOrAfter(today)) {
              active = true;
            }
            nextDays.push(
              {
                date: nextDay.toDate().getTime(),
                active: active
              } as DayToDisplay
            );
          }
          data.days = nextDays;

          nextDays.forEach(value => {
            value.events = [];
            const startOfDay = value.date
            const endOfDay = moment(startOfDay).add(1,'d').toDate().getTime();
            events.forEach( event => {
                if ( event.start >= value.date && event.start < endOfDay){
                    value.events.push(event);
                }
            } )
          });

          //
          // const until = nextDays[nextDays.length - 1].toDate().getTime();
          // const availableEvents = events.filter(value => {
          //   return value.start < until;
          // });
          //
          // nextDays.forEach(value => {
          //   data.days.push(value.toDate().getTime());
          // });

          // data.events = availableEvents;

          return of(new HttpResponse({status: 200, body: data}));

        }


        if (request.url.indexOf('/events') >= 0 && request.method === 'GET') {

          // check for a fake jwt token. If valid JWT token found, return the list of users, else throw error
          if (request.headers.get('Authorization') === 'Bearer 0000-fake-jwt-token-0000') {
            return of(new HttpResponse({status: 200, body: events}));
          } else {
            // invalid JWT token found in request header
            return throwError({
              error: {
                message: 'Unauthorized'
              }
            });
          }
        }

        const GET_EVENT = 'event&id_event=';
        if (request.url.indexOf(GET_EVENT) && request.method === 'GET') {
          // check for a fake jwt token. If valid JWT token found, return the list of users, else throw error
          if (request.headers.get('Authorization') === 'Bearer 0000-fake-jwt-token-0000') {
            let pos = request.url.indexOf(GET_EVENT);
            const length = GET_EVENT.length;
            pos = pos + length;
            let idEvent = request.url.substr(pos);
            let event = events.find(value => value.id == +idEvent);
            return of(new HttpResponse({status: 200, body: event}));
          } else {
            // invalid JWT token found in request header
            return throwError({
              error: {
                message: 'Unauthorized'
              }
            });
          }
        }


        if (request.headers.get('Authorization') === 'Bearer 0000-fake-jwt-token-0000') {
          if (request.url.indexOf('event/register&id_event=') && request.method === 'POST') {
            return of(new HttpResponse({status: 200, body: {}}));
          } else if (request.url.indexOf('event/cancel&id_event=') && request.method === 'POST') {
            return of(new HttpResponse({status: 200, body: {}}));
          }
        } else {
          // invalid JWT token found in request header
          return throwError({
            error: {
              message: 'Unauthorized'
            }
          });
        }

        // Pass any other requests left (unhandled
        return next.handle(request);
      })
    )
    // call materialize and dematerialize to ensure delay even if an error is thrown
      .pipe(materialize())
      .pipe(delay(100))
      .pipe(dematerialize());
  }

  createEventDate(plusDays: number, plusHours: number) {
    let date = new Date();
    date = new Date(date.getTime() + (plusDays * 1000 * 60 * 60 * 24));
    date.setHours(plusHours);
    return date.getTime();
  }
}

// creating a PROVIDER
export let fakeBackendProvider = {
  // use fake backend in place of Http service for backend-less development
  provide: HTTP_INTERCEPTORS,
  useClass: FakeBackendInterceptor,
  multi: true
};


