import { Card } from '../../shared/models/card.model';
import { Injectable } from '@angular/core';
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';
import { CardsData } from '../_helpers/mock-data';

@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {

    constructor() { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // array in local storage for registered users
        let users: any[] = JSON.parse(localStorage.getItem('users')) || [];

        // wrap in delayed observable to simulate server api call
        return of(null).pipe(mergeMap(() => {

            // authenticate
            if (request.url.endsWith('/users/authenticate') && request.method === 'POST') {
                // find if any user matches login credentials
                let filteredUsers = users.filter(user => {
                    return user.email === request.body.email && user.password === request.body.password;
                });

                if (filteredUsers.length) {
                    // if login details are valid return 200 OK with user details and fake jwt token
                    let user = filteredUsers[0];
                    let body = {
                        id: user.id,
                        email: user.email,
                        nome: user.nome,
                        sobrenome: user.sobrenome,
                        token: 'fake-jwt-token'
                    };

                    return of(new HttpResponse({ status: 200, body: body }));
                } else {
                    // else return 400 bad request
                    return throwError({ error: { message: 'Usuário ou senha incorretos' } });
                }
            }

            // get users
            if (request.url.endsWith('/users') && request.method === 'GET') {
                // check for fake auth token in header and return users if valid, this security is implemented server side in a real application
                // if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
                    return of(new HttpResponse({ status: 200, body: users }));
                // } else {
                //     // return 401 not authorised if token is null or invalid
                //     return throwError({ status: 401, error: { message: 'Unauthorised' } });
                // }
            }

            // get user by id
            if (request.url.match(/\/users\/\d+$/) && request.method === 'GET') {
                // check for fake auth token in header and return user if valid, this security is implemented server side in a real application
                // if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
                    // find user by id in users array
                    let urlParts = request.url.split('/');
                    let id = parseInt(urlParts[urlParts.length - 1]);
                    let matchedUsers = users.filter(user => { return user.id === id; });
                    let user = matchedUsers.length ? matchedUsers[0] : null;

                    return of(new HttpResponse({ status: 200, body: user }));
                // } else {
                //     // return 401 not authorised if token is null or invalid
                //     return throwError({ status: 401, error: { message: 'Unauthorised' } });
                // }
            }

            // register user
            if (request.url.endsWith('/users/register') && request.method === 'POST') {
                // get new user object from post body
                let newUser = request.body;

                // validation
                let duplicateUser = users.filter(user => { return user.email === newUser.email; }).length;
                if (duplicateUser) {
                    return throwError({ error: { message: 'email "' + newUser.email + '" is already taken' } });
                }

                // save new user
                newUser.id = users.length + 1;
                users.push(newUser);
                localStorage.setItem('users', JSON.stringify(users));

                // respond 200 OK
                return of(new HttpResponse({ status: 200 }));
            }

            // delete user
            if (request.url.match(/\/users\/\d+$/) && request.method === 'DELETE') {
                // check for fake auth token in header and return user if valid, this security is implemented server side in a real application
                // if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
                    // find user by id in users array
                    let urlParts = request.url.split('/');
                    let id = parseInt(urlParts[urlParts.length - 1]);
                    for (let i = 0; i < users.length; i++) {
                        let user = users[i];
                        if (user.id === id) {
                            // delete user
                            users.splice(i, 1);
                            localStorage.setItem('users', JSON.stringify(users));
                            break;
                        }
                    }

                    // respond 200 OK
                    return of(new HttpResponse({ status: 200 }));
                // } else {
                //     // return 401 not authorised if token is null or invalid
                //     return throwError({ status: 401, error: { message: 'Unauthorised' } });
                // }
            }

            if (request.url.endsWith('/alltags') && request.method === 'GET') {
                let tags: string[] = [];
                CardsData.DATA.forEach((data) => {
                    for (let tag of data.tags) {
                        tags.push(tag)
                    }
                })
                return of(new HttpResponse({ status: 200, body: tags }));
            }

            if (request.url.endsWith('/searchTags') && request.method === 'POST') {
                let searchTerms:string[] = request.body;
                if (!searchTerms || !searchTerms.length) {
                    return of(new HttpResponse({status: 410, body: []}));
                }
                let filteredCards:Card[] = [];

                for (let card of CardsData.DATA) {
                    let found = false;
                    searchTerms.forEach((term) => {
                        if (found) {
                            return;
                        }
                        if (card.nome.toLowerCase().includes(term.toLowerCase())) {
                            filteredCards.push(card);
                            found = true;
                        }
                        if (found) {
                            return;
                        }
                        for (let tag of card.tags) {
                            if (tag.toLowerCase().includes(term.toLowerCase())) {
                                filteredCards.push(card);
                                found = true;
                                break;
                            }
                        }
                    })
                }
                return of(new HttpResponse({status: 200, body: filteredCards}))
            }

            // pass through any requests not handled above
            return next.handle(request);
            
        }))

        // call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
        .pipe(materialize())
        .pipe(delay(500))
        .pipe(dematerialize());
    }
}

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