import type { AxiosError } from 'axios';
import type { SWRConfiguration, SWRResponse } from 'swr';
import useSWR from 'swr';

import type { FractalPaginatedResponse, PaginatedResult, QueryBuilderParams } from '@/api/http';
import http, { getPaginationSet, withQueryBuilderParams } from '@/api/http';
import type { User } from '@definitions/admin';
import { Transformers } from '@definitions/admin';

export interface UpdateUserValues {
    externalId: string;
    username: string;
    email: string;
    password: string;
    adminRoleId: number | null;
    rootAdmin: boolean;
}

const filters = ['id', 'uuid', 'external_id', 'username', 'email'] as const;
type Filters = (typeof filters)[number];

const useGetUsers = (
    params?: QueryBuilderParams<Filters>,
    config?: SWRConfiguration,
): SWRResponse<PaginatedResult<User>, AxiosError> => {
    return useSWR<PaginatedResult<User>>(
        ['/api/application/users', JSON.stringify(params)],
        async () => {
            const { data } = await http.get<FractalPaginatedResponse>('/api/application/users', {
                params: withQueryBuilderParams(params),
            });

            return {
                items: (data.data || []).map(Transformers.toUser),
                pagination: getPaginationSet(data.meta.pagination),
            };
        },
        config || { revalidateOnMount: true, revalidateOnFocus: false },
    );
};

const getUser = (id: number, include: string[] = []): Promise<User> => {
    return new Promise((resolve, reject) => {
        http.get(`/api/application/users/${id}`, { params: { include: include.join(',') } })
            .then(({ data }) => resolve(Transformers.toUser(data)))
            .catch(reject);
    });
};

const searchUserAccounts = async (params: QueryBuilderParams<'username' | 'email'>): Promise<User[]> => {
    const { data } = await http.get('/api/application/users', {
        params: withQueryBuilderParams(params),
    });

    return data.data.map(Transformers.toUser);
};

const createUser = (values: UpdateUserValues, include: string[] = []): Promise<User> => {
    const data = {};
    Object.keys(values).forEach(k => {
        // @ts-expect-error todo
        data[k.replace(/[A-Z]/g, l => `_${l.toLowerCase()}`)] = values[k];
    });

    return new Promise((resolve, reject) => {
        http.post('/api/application/users', data, { params: { include: include.join(',') } })
            .then(({ data }) => resolve(Transformers.toUser(data)))
            .catch(reject);
    });
};

const updateUser = (id: number, values: Partial<UpdateUserValues>, include: string[] = []): Promise<User> => {
    const data = {};
    Object.keys(values).forEach(k => {
        // Don't set password if it is empty.
        if (k === 'password' && values[k] === '') {
            return;
        }
        // @ts-expect-error todo
        data[k.replace(/[A-Z]/g, l => `_${l.toLowerCase()}`)] = values[k];
    });
    return new Promise((resolve, reject) => {
        http.patch(`/api/application/users/${id}`, data, { params: { include: include.join(',') } })
            .then(({ data }) => resolve(Transformers.toUser(data)))
            .catch(reject);
    });
};

const deleteUser = (id: number): Promise<void> => {
    return new Promise((resolve, reject) => {
        http.delete(`/api/application/users/${id}`)
            .then(() => resolve())
            .catch(reject);
    });
};

export { useGetUsers, getUser, searchUserAccounts, createUser, updateUser, deleteUser };