import { Injectable } from '@angular/core'

import { HttpClient, HttpHeaders } from '@angular/common/http'
import { Observable, BehaviorSubject, from, throwError } from 'rxjs'
import { map, catchError } from 'rxjs/operators'

import { User } from '../_models/user'
import { RoleReadNoUsers } from '../openapi'

interface AuthResponse {
    access_token: string,
    roles: string[]
}

@Injectable()
export class AuthenticationService {
    user = new BehaviorSubject<User>(undefined)
    user$ = this.user.asObservable()
    roles: RoleReadNoUsers[] = []

    constructor(
        private _http: HttpClient,
    ) {
        // set token if saved in local storage
        this.user.next(<User>JSON.parse(localStorage.getItem('user')))
    }

    isLoggedIn() : Observable<boolean> {
        if (!this.user.value) {
            return from([false])
        }
        let body = JSON.stringify({
            token: this.user.value.token,
        })
        return this._http.post(
            '/auth/isLoggedIn',
            body,
            {
                headers: new HttpHeaders({ 'Content-Type': 'application/json' })
            }
        ).pipe(
            map(resp => true),
            catchError(
                err => {
                    const userName = this.user.value['userName']
                    this.user.next(undefined)
                    this.roles = []
                    localStorage.removeItem('user')
                    return throwError(
                        () => new Error('Session of user "' + userName + '" expired.')
                    )
                }
            )
        )
    }

    login(userName: string, password: string): Observable<boolean> {
        let body = JSON.stringify({
            userName: userName,
            password: password
        })
        return this._http.post<AuthResponse>(
            '/auth/login',
            body,
            {
                headers: new HttpHeaders({ 'Content-Type': 'application/json' })
            }
        ).pipe(map(
            (response: AuthResponse) => {
                // login successful if there's a jwt token in the response
                let token = response.access_token
                if (token) {
                    //const decodedToken = this.helper.decodeToken(token)
                    // store userName and jwt token in local storage to keep user logged in between page refreshes
                    localStorage.setItem('user',
                        JSON.stringify({
                            userName: userName,
                            token: token,
                            roles: response.roles,
                        })
                    )

                    console.log('TODO: AuthenticationService roles to be set by refreshing bootstrap')
                    // this.roles = response.roles

                    // Notify
                    this.user.next(new User(userName, token))

                    // return true to indicate successful login
                    return true
                } else {
                    this.user.next(undefined)
                    this.roles = []
                    // return false to indicate failed login
                    return false
                }
            }
        ))
    }

    logout(): boolean {
        // XXX: not completly safe: the server might be down:
        //      We should actually *check* that the logout response is OK and display message
        // clear token remove user from local storage to log user out
        let has_token: boolean = this.user.value && !!this.user.value.token
        localStorage.removeItem('user')
        this.user.next(undefined)
        this.roles = []

        // Tell server that the user has logged out
        if (has_token) {
            this._http.get('/auth/logout').subscribe(response => {})
        }
        return has_token
    }

    logoutAdmin(): void {
    }

    isAuthorized(roles: string[]) {
        // Return true if at least one role in given list matches one role of the authenticated user
        if (roles.length == 0) return true
        return this.roles.filter(value => -1 !== roles.indexOf(value.name)).length > 0
    }
}
