import { createContext } from 'react';

export interface Model {
    relationships: Record<string, unknown>;
}

export type UUID = string;

/**
 * Marks the provided relationships keys as present in the given model
 * rather than being optional to improve typing responses.
 */
export type WithRelationships<M extends Model, R extends string> = Omit<M, 'relationships'> & {
    relationships: Omit<M['relationships'], keyof R> & {
        [K in R]: NonNullable<M['relationships'][K]>;
    };
};

/**
 * Helper type that allows you to infer the type of an object by giving
 * it the specific API request function with a return type. For example:
 *
 * type EggT = InferModel<typeof getEgg>;
 */
export type InferModel<T extends (...args: any) => any> = ReturnType<T> extends Promise<infer U> ? U : T;

/**
 * Helper function that just returns the model you pass in, but types the model
 * such that TypeScript understands the relationships on it. This is just to help
 * reduce the amount of duplicated type casting all over the codebase.
 */
export const withRelationships = <M extends Model, R extends string>(model: M, ..._keys: R[]) => {
    return model as unknown as WithRelationships<M, R>;
};

export interface ListContext<T> {
    page: number;
    setPage: (page: ((p: number) => number) | number) => void;

    filters: T | null;
    setFilters: (filters: ((f: T | null) => T | null) | T | null) => void;

    sort: string | null;
    setSort: (sort: string | null) => void;

    sortDirection: boolean;
    setSortDirection: (direction: ((p: boolean) => boolean) | boolean) => void;
}

function create<T>() {
    return createContext<ListContext<T>>({
        page: 1,
        setPage: () => 1,

        filters: null,
        setFilters: () => null,

        sort: null,
        setSort: () => null,

        sortDirection: false,
        setSortDirection: () => false,
    });
}

export { create as createContext };