import { DATASQUIREL_LoggedInUser, ServerQueryParam } from "../../types";
import EJSON from "../ejson";
import dsqlCrud from "./crud";

export const DataCrudRequestMethods = ["GET", "POST", "PUT", "DELETE"] as const;

export type APIDataCrudQuery = ServerQueryParam & {
    page?: number;
};

export type CRUDResponseObject<P extends any = any> = {
    success: boolean;
    payload?: P;
    msg?: string;
    error?: string;
};

export type ApiDataCrudParam<
    T extends { [key: string]: any } = { [key: string]: any }
> = {
    method: (typeof DataCrudRequestMethods)[number];
    body?: T;
    query?: string | T;
    tableName: string;
    addUser?: {
        field: string;
    };
    user?: DATASQUIREL_LoggedInUser;
    extraData?: T;
    transform?: ({
        data,
        existingData,
        user,
    }: {
        user?: DATASQUIREL_LoggedInUser;
        data: T;
        existingData?: T;
        reqMethod: (typeof DataCrudRequestMethods)[number];
    }) => Promise<T>;
    existingData?: T;
};

export default async function dsqlMethodCrud<
    T extends { [key: string]: any } = { [key: string]: any },
    P extends { [key: string]: any } = { [key: string]: any }
>({
    method,
    tableName,
    addUser,
    user,
    extraData,
    transform,
    existingData,
    body,
    query,
}: ApiDataCrudParam<T>): Promise<CRUDResponseObject<P>> {
    let result: CRUDResponseObject = {
        success: false,
    };

    try {
        let finalBody = body as any;
        let finalQuery = query as any;

        Object.keys(finalQuery).forEach((key) => {
            const value = finalQuery[key];
            if (typeof value == "string" && value.match(/^\{|^\[/)) {
                finalQuery[key] = EJSON.stringify(value);
            }
            if (value == "true") {
                finalQuery[key] = true;
            }
            if (value == "false") {
                finalQuery[key] = false;
            }
        });

        const LIMIT = finalQuery.limit || 10;
        const PAGE = finalQuery.page || 1;
        const OFFSET = (PAGE - 1) * LIMIT;

        let finalData = {
            ...finalBody.data,
            ...extraData,
        } as T;

        if (user && addUser) {
            finalData = {
                ...finalData,
                [addUser.field]: String(user.id),
            };
        }

        if (transform) {
            finalData = await transform({
                data: finalData,
                existingData: existingData,
                user,
                reqMethod: method as (typeof DataCrudRequestMethods)[number],
            });
        }

        switch (method) {
            case "GET":
                const GET_RESULT = await dsqlCrud({
                    action: "get",
                    table: tableName,
                    query: {
                        ...finalQuery,
                        query: {
                            ...finalQuery.query,
                            user_id: user
                                ? {
                                      value: String(user.id),
                                  }
                                : undefined,
                        },
                        limit: LIMIT,
                        offset: OFFSET || undefined,
                    },
                });

                result = {
                    success: Boolean(GET_RESULT?.success),
                    payload: GET_RESULT?.payload,
                    msg: GET_RESULT?.msg,
                    error: GET_RESULT?.error,
                };
                break;

            case "POST":
                const POST_RESULT = await dsqlCrud({
                    action: "insert",
                    table: tableName,
                    data: finalData,
                });
                result = {
                    success: Boolean(POST_RESULT?.success),
                    payload: POST_RESULT?.payload,
                    msg: POST_RESULT?.msg,
                    error: POST_RESULT?.error,
                };
                break;

            case "PUT":
                const PUT_RESULT = await dsqlCrud({
                    action: "update",
                    table: tableName,
                    data: finalData,
                    targetId: finalBody.data.id,
                });
                result = {
                    success: Boolean(PUT_RESULT?.success),
                    payload: PUT_RESULT?.payload,
                    msg: PUT_RESULT?.msg,
                    error: PUT_RESULT?.error,
                };
                break;
            case "DELETE":
                const DELETE_RESULT = await dsqlCrud({
                    action: "delete",
                    table: tableName,
                    targetId: finalBody.data.id,
                });
                result = {
                    success: Boolean(DELETE_RESULT?.success),
                    payload: DELETE_RESULT?.payload,
                    msg: DELETE_RESULT?.msg,
                    error: DELETE_RESULT?.error,
                };
                break;

            default:
                break;
        }
        return result;
    } catch (error) {
        return result;
    }
}