export type MobileUser = {
    id: number;
    name: string | null;
    account_number: string;
    flat_no: string | null;
    phone: string | null;
    email: string | null;
    balance: number | null;
    phone_balance: number | null;
    tag_debt: number | null;
    roles: string[];
};

export type LoginResponse = {
    token: string;
    token_type: "Bearer";
    user: MobileUser;
};

type ApiErrorPayload = {
    message?: string;
    errors?: Record<string, string[]>;
};

export class ApiError extends Error {
    constructor(
        message: string,
        public readonly status: number,
        public readonly payload?: ApiErrorPayload,
    ) {
        super(message);
        this.name = "ApiError";
    }
}

export const API_BASE_URL =
    process.env.EXPO_PUBLIC_API_URL ?? "https://elevator.ddev.site";

export async function loginCustomer(params: {
    accountNumber: string;
    password: string;
    deviceName: string;
}): Promise<LoginResponse> {
    const response = await fetch(apiUrl("/api/mobile/login"), {
        method: "POST",
        headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            account_number: params.accountNumber,
            password: params.password,
            device_name: params.deviceName,
        }),
    });

    return parseJsonResponse<LoginResponse>(response);
}

export async function logoutCustomer(token: string): Promise<void> {
    const response = await fetch(apiUrl("/api/mobile/logout"), {
        method: "DELETE",
        headers: {
            Accept: "application/json",
            Authorization: `Bearer ${token}`,
        },
    });

    await parseJsonResponse<null>(response);
}

async function parseJsonResponse<T>(response: Response): Promise<T> {
    const payload = (await parsePayload(response)) as ApiErrorPayload | T | null;

    if (!response.ok) {
        throw new ApiError(
            firstErrorMessage(payload) ?? "Request failed.",
            response.status,
            (payload ?? undefined) as ApiErrorPayload | undefined,
        );
    }

    return payload as T;
}

async function parsePayload(response: Response): Promise<unknown> {
    const text = await response.text();

    if (text.length === 0) {
        return null;
    }

    try {
        return JSON.parse(text);
    } catch {
        return null;
    }
}

function apiUrl(path: string): string {
    return `${API_BASE_URL.replace(/\/+$/, "")}${path}`;
}

function firstErrorMessage(payload: ApiErrorPayload | unknown): string | null {
    if (!payload || typeof payload !== "object") {
        return null;
    }

    const errorPayload = payload as ApiErrorPayload;
    const firstErrors = errorPayload.errors
        ? Object.values(errorPayload.errors).find((messages) => messages.length > 0)
        : undefined;

    return firstErrors?.[0] ?? errorPayload.message ?? null;
}
