/*
 * Copyright 2017-2025 VMware, Inc. All rights reserved. VMware Confidential
 */
import { Inject, Injectable } from "@angular/core";
import { AuthTokenData } from "./common/models/auth";
import { AuthTokenHolderService } from "./common/services/auth-token-holder.service";
import { HYPER_PLUGIN_CONFIG, HyperpluginConfig } from "../lib-interfaces";

const AuthTokenKeys = {
    AUTH: "authtoken",
    AUTH_CONTEXT: "authcontext",
    JWT: "jwt",
    USER: "username",
};

const { AUTH, AUTH_CONTEXT, JWT, USER } = AuthTokenKeys;

/**
 * Holds all the authentication values needed to access vCD backed by StorageService.
 */
@Injectable()
export class VcdAuthTokenHolderService extends AuthTokenHolderService {
    private cacheAuthTokenData: AuthTokenData;

    constructor(
        @Inject(HYPER_PLUGIN_CONFIG) private HYPER_PLUGIN_CONFIG: HyperpluginConfig,
    ) {
        super();
        this.populateCachedAuthTokenData(this.HYPER_PLUGIN_CONFIG);
    }

    /**
     * Populate a local AuthTokenData object using data currently in local storage
     */
    private populateCachedAuthTokenData(HYPER_PLUGIN_CONFIG?: HyperpluginConfig): void {
        if (HYPER_PLUGIN_CONFIG.tokenHolder && HYPER_PLUGIN_CONFIG.tokenHolder.localStorage) {
            const jwt = this.getValueOfItem(HYPER_PLUGIN_CONFIG.tokenHolder.localStorage[JWT]);
            const authContext = this.getValueOfItem(HYPER_PLUGIN_CONFIG.tokenHolder.localStorage[AUTH_CONTEXT]);
            const username = this.getValueOfItem(HYPER_PLUGIN_CONFIG.tokenHolder.localStorage[USER]);

            this.cacheAuthTokenData = {
                authToken: "",
                jwt: jwt,
                authContext: authContext,
                username: username,
            };
            
            return;
        }
        this.cacheAuthTokenData = {
            authToken: window.localStorage.getItem(AUTH),
            jwt: window.localStorage.getItem(JWT),
            authContext: window.localStorage.getItem(AUTH_CONTEXT),
            username: window.localStorage.getItem(USER),
        };
    }

    /**
     * Updates the auth data in local storage and the cached version of the same data.
     * @param token - obsolete @see VCLOUD_AUTHORIZATION_HEADER
     * @param username - the current user name
     * @param authContext the authorization context
     * @param jwt - the jwt token
     */
    public update(token: string, username: string, authContext: string, jwt: string): void {
        // pass
    }

    public deleteToken(): void {
       // pass
    }

    get authTokenData(): AuthTokenData {
        return this.cacheAuthTokenData;
    }

    get token(): string {
        return this.cacheAuthTokenData.authToken;
    }

    set token(token: string) {
        // pass
    }

    get username(): string {
        return this.cacheAuthTokenData.username;
    }

    get jwt(): string {
        return this.cacheAuthTokenData.jwt;
    }

    set jwt(token: string) {
        // pass
    }

    get authorizationHeader(): string | undefined {
        return this.cacheAuthTokenData.jwt;
    }

    get authContext(): string {
        return this.cacheAuthTokenData.authContext;
    }

    set authContext(authContext: string) {
        // pass
    }

    private getValueOfItem(item: {
        propertyPath: string;
        fallbackPropertyPath?: string;
        defaultValue?: string;
    }): string {
        // Flag used to determine do we use fallback property or not.
        let errored = false;

        // Get all properties that need to be fetched from localStorage and traversed.
        let propertyPathSegments = item.propertyPath.split(".");
        let rootSegment = propertyPathSegments.shift();
        let root = window.localStorage.getItem(rootSegment);

        // If there are no other segments return the value taken from localStorage
        if (!propertyPathSegments.length) {
            return root;
        }

        // If there are more segments means the value taken from the localStorage a JSON object
        let jsonObject: any;
        try {
            jsonObject = JSON.parse(root)
        } catch(e) {
            console.error(e);
            errored = true;
        }

        if (errored && !item.fallbackPropertyPath) {
            return item.defaultValue || "";
        }

        // If error occur retry to get the data from localStorage but with fallbacl property path
        if (errored) {
            // Use fallback property path
            propertyPathSegments = item.fallbackPropertyPath.split(".");
            rootSegment = propertyPathSegments.shift();
            root = window.localStorage.getItem(rootSegment);

            if (!propertyPathSegments.length) {
                return root;
            }

            try {
                jsonObject = JSON.parse(root)
            } catch(e) {
                // Final error, return immediately default value or empty string
                console.error(e);
                return item.defaultValue || "";
            }
        }
        
        // Store the final string value here
        let value = jsonObject;
        try {
            // Traverse the properties from the property path, final property is the final value.
            while (propertyPathSegments.length) {
                const propertyPathSegment = propertyPathSegments.shift();
                value = value[propertyPathSegment];
            }
        } catch(e) {
            // If error occurred before return immediately default value or empty string, fallback is already in use
            if (errored) {
                console.error(e);
                return item.defaultValue || "";
            }

            if (!item.fallbackPropertyPath) {
                return item.defaultValue || "";
            }

            // If no error occurred use fallback property path
            propertyPathSegments = item.fallbackPropertyPath.split(".");
            // Remove the 1st property segment because we already have the root.
            propertyPathSegments.shift();

            value = jsonObject;
            // Traverse the properties from the fallback property path, final property is the final value.
            try {
                while (propertyPathSegments.length) {
                    const propertyPathSegment = propertyPathSegments.shift();
                    value = jsonObject[propertyPathSegment];
                }
            } catch(e) {
                console.error(e);
                return item.defaultValue || "";
            }
        }

        return value as string;
    }
}
