import axios, { AxiosResponse, isAxiosError } from 'axios';
import { RoleName } from "./types/policy.js";
import { UserId } from "./types/user.js";

class Archon {
    private static instance: Archon;
    private serviceToken?: string;
    private userId?: UserId;
    private roleName?: RoleName;
    private rootUrl: string = "http://engine:3000";
    private publicUrl: string = "http://localhost:8333";

    private componentName: string = "UNSET_NAME";

    private sessionId?: string;

    constructor() {
        if (Archon.instance) {
            return Archon.instance;
        }
        Archon.instance = this;
    }

    public setServiceToken(token: string): void {
        this.serviceToken = token;
    }

    public setUserUuid(uuid: UserId): void {
        this.userId = uuid;
    }

    public setRoleName(role: RoleName): void {
        this.roleName = role;
    }

    public setComponentName(name: string): void {
        this.componentName = name;
    }

    public getComponentName(): string {
        return this.componentName;
    }

    public getPublicUrl(): string {
        return this.publicUrl;
    }

    private async authenticate(): Promise<void> {
        if (!this.serviceToken) {
            // try to load the service token from the environment
            if (process?.env?.SERVICE_TOKEN) {
                this.serviceToken = process.env.SERVICE_TOKEN;
            } else {
                throw new Error("Service token not provided");
            }
        }

        const payload: { [key: string]: string } = {
            serviceToken: this.serviceToken,
        }
        if (process.env.ARCHON_SERVICE_USERNAME && process.env.ARCHON_SERVICE_ROLENAME) {
            payload["username"] = process.env.ARCHON_SERVICE_USERNAME;
            payload["roleName"] = process.env.ARCHON_SERVICE_ROLENAME;
        } else if (this.userId && this.roleName) {
            payload["userId"] = this.userId;
            payload["roleName"] = this.roleName;
        } else {
            throw new Error("Credentials not provided");
        }

        try {
            const response = await axios({
                method: "POST",
                url: `${this.rootUrl}/local/serviceAuth`,
                headers: {
                    'Content-Type': 'application/json',
                },
                data: payload
            })

            this.sessionId = response.data.session_id;
        } catch (error) {
            if (isAxiosError(error)) {
                console.error("Error: " + error.response?.data);
                console.error("Status " + error.response?.status);
            } else {
                console.error(error);
            }
        }
    }

    public async request(route: string, method: string, body?: object): Promise<AxiosResponse> {
        if (!this.sessionId) {
            await this.authenticate();
        }

        try {
            const response = await axios({
                method: method,
                url: `${this.rootUrl}${route}`,
                headers: {
                    // if its not a get request, make the content type application/json
                    ...(method ? { 'Content-Type': 'application/json' } : {}),
                    "x-session-id": this.sessionId,
                },
                data: body,
            })

            return response;
        } catch (error) {
            if (isAxiosError(error)) {
                throw new Error(`Error (${error.response?.status || "unknown"}): ${error.response?.data || "Request failed"}`);
            }

            throw error;
        }

    }
}

const archonInstance = new Archon();
export {
    archonInstance as Archon
};
