import axios from 'axios';

class SuperboxApi {

    private readonly defaultPort: number = 8000;

    private readonly apiLogin: string = "/auth/login";
    private readonly apiLogout: string = "/auth/logout";
    private readonly apiAdduser: string = "/auth/adduser";
    private readonly apiRmuser: string = "/auth/rmuser";
    private readonly apiAllusers: string = "/auth/allusers";
    private readonly apiCameras: string = "/cameras";
    private readonly apiEvents: string = "/events";
    private readonly apiAlgorithms: string = "/algorithms";

    private readonly loginConfig: object = {
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        }
    }

    private address: string = "";
    private port: number = this.defaultPort;
    private token: string = "";

    private codemap: Map<string, string> = new Map<string, string>();
    private axios: any = null;

    public constructor(
        address: string = "",
        port: number = 0
    ) { 
        this.setAddress(address);
        this.setPort(port);
        this.axios = axios.create({
            baseURL: `http://${this.address}:${this.port}/api/v1`,
            withCredentials: true
        })
    }

    public setAddress(address: string) {
        this.address = address === "" ? this._boxAddr() : address;
    }

    public setPort(port: number) {
        this.port = port === 0 ? this.defaultPort : port;
    }

    public setToken(token: string) {
        this.token = token;
    }

    public async login(username: string, password: string, cookieLess: boolean = false): Promise<any> {
        const loginData = {
            username: username,
            password: password,
            cookieless: cookieLess ? "true" : "false"  // 这里会根据 cookieLess 的值决定是 "true" 还是 "false"
        };
    
        try {
            const res = await this.axios.post(this.apiLogin, loginData, this.loginConfig)
            if (res.data.err.ec === 0) {
                this.token = res.data.ret.token;
                await this.updateCodemap(this.token);
                return res.data.ret.token;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
    }

    public async logout(token: string | null = null): Promise<any> {
        try {
            const res = await this.axios.post(this.apiLogout, this._authHeader(token));
            if (res.data.err.ec === 0) {
                return res.data;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
    }

    public async addUser(username: string, password: string, email: string="", token: string | null = null): Promise<any> {
        try {
            const data = {
                username: username,
                password: password,
                email: email
            };

            const res = await this.axios.post(this.apiAdduser, data, this._authHeader(token));
            if (res.data.err.ec === 0) {
                return res.data.ret;
            } else {
                throw new Error(res.data.err.msg);
           }
        } catch (error) {
            throw error;
        } 
   }

   public async rmUser(username: string, token: string | null = null): Promise<any> {
        try {
            const data = {
                username: username
            };

            const res = await this.axios.post(this.apiRmuser, data, this._authHeader(token));
            if (res.data.err.ec === 0) {
                return res.data.ret;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
   }

   public async getAllUsers(token: string | null = null): Promise<any> {
        try {
            const res = await this.axios.get(this.apiAllusers, this._authHeader(token));
            if (res.data.err.ec === 0) {
                return res.data.ret.users;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
   }

    public async getCamerasByUrl(url: string, token: string | null = null): Promise<any> {
        try {
            const res = await this.axios.get(url, this._authHeader(token));
            if (res.data.err.ec === 0) {
                return res.data.ret;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
    }

    public async getCameras(limit: number, offset: number, token: string | null = null): Promise<any> {
        const url = `${this.apiCameras}?limit=${limit}&offset=${offset}`;
        return await this.getCamerasByUrl(url, token);
    }

    public async getEventsByUrl(url: string, token: string | null = null): Promise<any> {
        try {
            const res = await this.axios.get(url, this._authHeader(token));
            if (res.data.err.ec === 0) {
                return res.data.ret;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
    }

    public async getEvents(limit: number, offset: number, token: string | null = null): Promise<any> {
        const url = `${this.apiEvents}?limit=${limit}&offset=${offset}`;
        return await this.getEventsByUrl(url, token);
    }

    public async getOneEvent(token: string | null = null): Promise<any> {
        try {
            return await this.getEvents(1, 0, token);
        } catch (error) {
            throw error;
        }
    }


    public async setEventStatus(eventId: number, status: string, remark: string | null = null, token: string | null = null): Promise<any> {
        const url = `${this.apiEvents}/${eventId}`;
        const newRemark = remark ? remark : "";

        const data = {
            status: status,
            remark: newRemark
        };

        try {
            const res = await this.axios.patch(url, data, this._authHeader(token))
            if (res.data.err.ec === 0) {
                return res.data.ret;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
    }

    public async getAlgorithms(token: string | null = null): Promise<any> {
        try {
            const res = await this.axios.get(this.apiAlgorithms, this._authHeader(token))
            if (res.data.err.ec === 0) {
                return res.data.ret;
            } else {
                throw new Error(res.data.err.msg);
            }
        } catch (error) {
            throw error;
        }
    }

    public async updateCodemap(token: string | null = null): Promise<boolean> {
        try {
            this.codemap.clear()
            const algorithms = await this.getAlgorithms(token);
            algorithms.forEach((algorithm: { code_name: string, name: string }) => {
                this.codemap.set(algorithm.code_name, algorithm.name)
            });
            return true;
        } catch (error) {
            return false;
        }
    }

    public getAlgorithmName(code_name: string): string {
        return this.codemap.get(code_name) || code_name;
    }

    private _authHeader(token: string | null = null): object {
        const access_token = token === "" ? this.token : token;
        return {
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'Authorization': `Bearer ${access_token}`,
            }
        }
    }

    private _boxAddr() {
        return window.location.host.replace(/:\d+/, "");
    }

}

export { SuperboxApi };
