import { useProgressStore } from "../store/ProgressStore";
import { useUserStore } from "../store/UserStore";
interface ApiProgressData {
    loaded: number;
    total: number;
}

type ApiProgressCallback = (progress: ApiProgressData) => void;

interface ApiOptions {
    url: string;
    params?: object;
    method?: string;
    headers?: HeadersInit;
    body?: string | object;
    signal?: AbortSignal | null;
    progress?: ApiProgressCallback;
}

export interface ApiResponse {
    status: number;
    message: string;
    data: unknown;
    json?: Record<string, unknown>;
}

const apiDefaultHeaders = {
    Accept: "application/json",
    "Content-Type": "application/json;charset=UTF-8",
};

export const api = {
    timeout: 8000,
    baseUrl: "https://www.stemmechanics.com.au/api",

    send: function (options: ApiOptions) {
        return new Promise((resolve, reject) => {
            let url = this.baseUrl + options.url;

            if (options.params) {
                let params = "";

                for (const [key, value] of Object.entries(options.params)) {
                    const placeholder = `{${key}}`;
                    if (url.includes(placeholder)) {
                        url = url.replace(placeholder, value);
                    } else {
                        params += `&${key}=${value}`;
                    }
                }

                url = url.replace(/{(.*?)}/g, "$1");
                if (params.length > 0) {
                    url += (url.includes("?") ? "" : "?") + params.substring(1);
                }
            }

            options.headers = {
                ...apiDefaultHeaders,
                ...(options.headers || {}),
            };

            const userStore = useUserStore();
            if (userStore.id) {
                options.headers["Authorization"] = `Bearer ${userStore.token}`;
            }

            if (options.body && typeof options.body === "object") {
                if (options.body instanceof FormData) {
                    if (
                        Object.prototype.hasOwnProperty.call(
                            options.headers,
                            "Content-Type"
                        )
                    ) {
                        // remove the "Content-Type" key from the headers object
                        delete options.headers["Content-Type"];
                    }
                } else {
                    options.body = JSON.stringify(options.body);
                }
            }

            const fetchOptions: RequestInit = {
                method: options.method || "GET",
                headers: options.headers,
                signal: options.signal || null,
            };

            if (
                (typeof options.body == "string" && options.body.length > 0) ||
                options.body instanceof FormData
            ) {
                fetchOptions.body = options.body;
            }

            const progressStore = useProgressStore();
            progressStore.start();

            fetch(url, fetchOptions)
                .then(async (response) => {
                    let data: string | object = "";
                    if (response.headers.get("content-type") == null) {
                        try {
                            data = response.json ? await response.json() : {};
                        } catch (error) {
                            data = response.text ? await response.text() : "";
                        }
                    } else {
                        data = response.json ? await response.json() : {};
                    }

                    const result = {
                        status: response.status,
                        statusText: response.statusText,
                        url: response.url,
                        headers: response.headers,
                        data: data,
                    };

                    if (response.status >= 300) {
                        reject(result);
                    }

                    resolve(result);
                })
                .catch((error) => {
                    // Handle any errors thrown during the fetch process
                    const { response, ...rest } = error;
                    reject({
                        ...rest,
                        response: response && response.json(),
                    });
                })
                .finally(() => {
                    progressStore.finish();
                });
        });
    },

    get: async function (options: ApiOptions | string): Promise<ApiResponse> {
        let apiOptions = {} as ApiOptions;

        if (typeof options == "string") {
            apiOptions.url = options;
        } else {
            apiOptions = options;
        }

        apiOptions.method = "GET";
        return await this.send(apiOptions);
    },

    post: async function (options: ApiOptions | string): Promise<ApiResponse> {
        let apiOptions = {} as ApiOptions;

        if (typeof options == "string") {
            apiOptions.url = options;
        } else {
            apiOptions = options;
        }

        apiOptions.method = "POST";
        return await this.send(options);
    },

    put: async function (options: ApiOptions | string): Promise<ApiResponse> {
        let apiOptions = {} as ApiOptions;

        if (typeof options == "string") {
            apiOptions.url = options;
        } else {
            apiOptions = options;
        }

        apiOptions.method = "PUT";
        return await this.send(options);
    },

    delete: async function (
        options: ApiOptions | string
    ): Promise<ApiResponse> {
        let apiOptions = {} as ApiOptions;

        if (typeof options == "string") {
            apiOptions.url = options;
        } else {
            apiOptions = options;
        }

        apiOptions.method = "DELETE";
        return await this.send(options);
    },
};
