import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http';
import {Injectable, OnDestroy} from '@angular/core';
import {map, tap} from 'rxjs/operators';
import {Authorization} from '../models/Authorization';
import {LocalUserDTO} from "../models/enea/LocalUserDTO";

import {Observable, Subscription} from "rxjs";
import {UtilsService} from './utils.service';
import {environment} from "../../environments/environment";
import {AuthConfig, OAuthService} from "angular-oauth2-oidc";
import {Community} from "../models/Community";
import {User} from "../models/User";
import {AuthService} from "./auth.service";
import {Router} from "@angular/router";

@Injectable({
    providedIn: 'root'
})
export class UserService implements OnDestroy {

    user: User = new User();
    community: Community = new Community();
    selectedCommunity: Number = -1;

    private readonly authSubscription?: Subscription;

    constructor(private http: HttpClient, private router: Router, private auth: AuthService, private utils: UtilsService, private oauthService: OAuthService) {
        this.authSubscription = this.auth.onAuthChange().subscribe((authorization) => {
            this.user = authorization?.user;

            this.selectedCommunity = this.user?.usercommunities[0].communityid! ? +this.user?.usercommunities[0].communityid! : 0;
            this.community = this.user?.usercommunities[0] ? this.user?.usercommunities[0] : new Community();
        })
        auth.initAuthorization();

    }

    ngOnDestroy(): void {
        if (this.authSubscription)
            this.authSubscription.unsubscribe();
    }

    public initOpenId() {
        const authConfig: AuthConfig = {
            issuer: environment.oidIssuer,
            redirectUri: window.location.origin + "/login-oid",
            logoutUrl: window.location.origin,
            clientId: environment.oidClient,
            dummyClientSecret: environment.oidSecret,
            responseType: environment.oidResponseType,
            requireHttps: environment.oidHttps,
            scope: environment.oidScope,
            showDebugInformation: environment.oidDebug,
            // turn off validation that discovery document endpoints start with the issuer url defined above
            // https://manfredsteyer.github.io/angular-oauth2-oidc/docs/additional-documentation/using-an-id-provider-that-fails-discovery-document-validation.html
            strictDiscoveryDocumentValidation: false,
            skipIssuerCheck: true,
        };
        this.oauthService.configure(authConfig);
        this.oauthService.requireHttps = false;

        this.oauthService.loadDiscoveryDocumentAndLogin()
            .then(o => {
                console.info("loadDiscoveryDocumentAndTryLogin() -> ", o);
            })
            .catch(err => console.error(err));
    }

    login() {
        if (environment.authenticationType === 'oid') {
            console.info("OpenId login: ", environment.oidProvider);
            if (!this.oauthService.hasValidAccessToken())
                this.initOpenId();
            else {
                const token = this.oauthService.getAccessToken();
                if (!environment.production) console.info("Sign In token: ", token);
                // IMPORTANT: Subscribe is need to continue the login flow. If removed user will be stuck in oid redirect page
                this.loginWithToken(token).subscribe({
                    next: result => this.router.navigateByUrl(""),
                    error: err => console.error(err)
                });
            }
        } else {
            console.info("Local login configured");
            window.location.href = environment.authenticationUrl;
        }
    }

    localLogin(username: string, password: string) {
        const httpOptions = {
            headers: new HttpHeaders({'Content-Type': 'application/json'})
        };

        return this.http.post(environment.baseUrl + "/lecservice/auth/authenticate", {username: username, password: password}, httpOptions)
            .pipe(
                tap(
                    (serviceResponse: any) => {
                        if (!serviceResponse.success)
                            throw new Error("Authentication failed.");

                        this.setAuthObjects(serviceResponse.result);
                    })
            );
    }

    loginWithToken(token: string) {
        const httpOptions = {
            headers: new HttpHeaders({'Content-Type': 'application/json'})
        };
        return this.http.post(environment.baseUrl + "/lecservice/auth/authenticateWithToken", {token: token}, httpOptions)
            .pipe(
                tap(
                    (serviceResponse: any) => {
                        if (!serviceResponse.success) {
                            this.utils.showInfoMessage("Authentication with a token failed!");
                            throw new Error("Authentication failed.");
                        }

                        this.setAuthObjects(serviceResponse.result);
                        //this.router.navigate(['']).then(r => console.info("Welcome " + this.user?.username));
                    })
            );
    }

    activate(code: string) {
        const httpOptions = {
            headers: new HttpHeaders({'Content-Type': 'application/json'})
        };

        return this.http.post(
            environment.baseUrl + "/lecservice/account/activate",
            {code}, httpOptions
        );
    }

    logout() {
        if (environment.authenticationType === 'oid') {
            return new Observable(o => {
                if (this.oauthService.hasValidAccessToken()) {
                    this.oauthService.logOut(true);
                    //this.oauthService.revokeTokenAndLogout();
                    o.next();
                }

                o.complete();
            });
        } else {
            console.info("Local logout");
            return this.http.post(environment.baseUrl + "/lecservice/auth/logout", {})
                .pipe(
                    tap(
                        (serviceResponse: any) => {
                            if (!serviceResponse.success)
                                throw new Error("Logout failed.")
                            console.info("Logged out")
                        },
                        err => {
                            console.error("Error during logout: ", err)
                        }
                    )
                );
        }
    }

    getUserFullName() {
        this.http.get(environment.baseUrl + "/lecservice/admin")
            .subscribe(
                data => {
                    console.log("getUserFullName: ", data);
                    alert(data);
                },
                (err: HttpErrorResponse) => {
                    console.error('An error occurred:', err.error);
                }
            );
    }

    getCommunities(): Observable<any> {
        return this.http.get(environment.baseUrl + "/lecservice/listCommunities");
    }

    register(model: LocalUserDTO): Observable<any> {
        return this.http.post(environment + "/lecservice/register", model).pipe(
            map((response: any) => {
                return response.result
            })
        );
    }

    updatePassword(model: LocalUserDTO): Observable<any> {
        return this.http.post(environment.baseUrl + "/lecservice/updatePassword", model).pipe(
            map((response: any) => {
                return response.result
            })
        );
    }

    update(model: LocalUserDTO): Observable<any> {
        return this.http.post(environment.baseUrl + "/lecservice/update", model).pipe(
            map((response: any) => {
                return response.result
            })
        );
    }

    private setAuthObjects(result: any) {
        this.auth.setAuthorization(
            new Authorization({
                authenticated: true,
                token: result.token,
                user: result.user
            })
        );
    }
}
