import type { ChangeEvent } from 'react';
import { useContext, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
import tw from 'twin.macro';

import type { Filters } from '@/api/admin/nodes/allocations/getAllocations';
import getAllocations, { Context as AllocationsContext } from '@/api/admin/nodes/allocations/getAllocations';
import AdminCheckbox from '@/components/admin/AdminCheckbox';
import AdminTable, {
    ContentWrapper,
    Loading,
    NoItems,
    Pagination,
    TableBody,
    TableHead,
    TableHeader,
    useTableHooks,
} from '@/components/admin/AdminTable';
import DeleteAllocationButton from '@/components/admin/nodes/allocations/DeleteAllocationButton';
import CopyOnClick from '@/components/elements/CopyOnClick';
import useFlash from '@/plugins/useFlash';
import { AdminContext } from '@/state/admin';

function RowCheckbox({ id }: { id: number }) {
    const isChecked = AdminContext.useStoreState(state => state.allocations.selectedAllocations.indexOf(id) >= 0);
    const appendSelectedAllocation = AdminContext.useStoreActions(
        actions => actions.allocations.appendSelectedAllocation,
    );
    const removeSelectedAllocation = AdminContext.useStoreActions(
        actions => actions.allocations.removeSelectedAllocation,
    );

    return (
        <AdminCheckbox
            name={id.toString()}
            checked={isChecked}
            onChange={(e: ChangeEvent<HTMLInputElement>) => {
                if (e.currentTarget.checked) {
                    appendSelectedAllocation(id);
                } else {
                    removeSelectedAllocation(id);
                }
            }}
        />
    );
}

interface Props {
    nodeId: number;
    filters?: Filters;
}

function AllocationsTable({ nodeId, filters }: Props) {
    const { clearFlashes, clearAndAddHttpError } = useFlash();

    const { page, setPage, setFilters, sort, setSort, sortDirection } = useContext(AllocationsContext);
    const { data: allocations, error, isValidating, mutate } = getAllocations(nodeId, ['server']);

    const length = allocations?.items?.length || 0;

    const setSelectedAllocations = AdminContext.useStoreActions(actions => actions.allocations.setSelectedAllocations);
    const selectedAllocationLength = AdminContext.useStoreState(state => state.allocations.selectedAllocations.length);

    const onSelectAllClick = (e: ChangeEvent<HTMLInputElement>) => {
        setSelectedAllocations(
            e.currentTarget.checked ? allocations?.items?.map?.(allocation => allocation.id) || [] : [],
        );
    };

    const onSearch = (query: string): Promise<void> => {
        return new Promise(resolve => {
            if (query.length < 2) {
                setFilters(filters || null);
            } else {
                setFilters({ ...filters, ip: query });
            }
            return resolve();
        });
    };

    useEffect(() => {
        setSelectedAllocations([]);
    }, [page]);

    useEffect(() => {
        if (!error) {
            clearFlashes('allocations');
            return;
        }

        clearAndAddHttpError({ key: 'allocations', error });
    }, [error]);

    return (
        <AdminTable>
            <ContentWrapper
                checked={selectedAllocationLength === (length === 0 ? -1 : length)}
                onSelectAllClick={onSelectAllClick}
                onSearch={onSearch}
            >
                <Pagination data={allocations} onPageSelect={setPage}>
                    <div css={tw`overflow-x-auto`}>
                        <table css={tw`w-full table-auto`}>
                            <TableHead>
                                <TableHeader
                                    name={'IP Address'}
                                    direction={sort === 'ip' ? (sortDirection ? 1 : 2) : null}
                                    onClick={() => setSort('ip')}
                                />
                                <TableHeader name={'Alias'} />
                                <TableHeader
                                    name={'Port'}
                                    direction={sort === 'port' ? (sortDirection ? 1 : 2) : null}
                                    onClick={() => setSort('port')}
                                />
                                <TableHeader name={'Assigned To'} />
                                <TableHeader />
                            </TableHead>

                            <TableBody>
                                {allocations !== undefined &&
                                    !error &&
                                    !isValidating &&
                                    length > 0 &&
                                    allocations.items.map(allocation => (
                                        <tr key={allocation.id} css={tw`h-10 hover:bg-neutral-600`}>
                                            <td css={tw`pl-6`}>
                                                <RowCheckbox id={allocation.id} />
                                            </td>

                                            <td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
                                                <CopyOnClick text={allocation.ip}>
                                                    <code css={tw`font-mono bg-neutral-900 rounded py-1 px-2`}>
                                                        {allocation.ip}
                                                    </code>
                                                </CopyOnClick>
                                            </td>

                                            {allocation.alias !== null ? (
                                                <td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
                                                    <CopyOnClick text={allocation.alias}>
                                                        <code css={tw`font-mono bg-neutral-900 rounded py-1 px-2`}>
                                                            {allocation.alias}
                                                        </code>
                                                    </CopyOnClick>
                                                </td>
                                            ) : (
                                                <td />
                                            )}

                                            <td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
                                                <CopyOnClick text={allocation.port}>
                                                    <code css={tw`font-mono bg-neutral-900 rounded py-1 px-2`}>
                                                        {allocation.port}
                                                    </code>
                                                </CopyOnClick>
                                            </td>

                                            {allocation.relations.server !== undefined ? (
                                                <td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
                                                    <NavLink
                                                        to={`/admin/servers/${allocation.serverId}`}
                                                        css={tw`text-primary-400 hover:text-primary-300`}
                                                    >
                                                        {allocation.relations.server.name}
                                                    </NavLink>
                                                </td>
                                            ) : (
                                                <td />
                                            )}

                                            <td>
                                                <DeleteAllocationButton
                                                    nodeId={nodeId}
                                                    allocationId={allocation.id}
                                                    onDeleted={async () => {
                                                        await mutate(allocations => ({
                                                            pagination: allocations!.pagination,
                                                            items: allocations!.items.filter(
                                                                a => a.id === allocation.id,
                                                            ),
                                                        }));

                                                        // Go back a page if no more items will exist on the current page.
                                                        if (allocations?.items.length - (1 % 10) === 0) {
                                                            setPage(p => p - 1);
                                                        }
                                                    }}
                                                />
                                            </td>
                                        </tr>
                                    ))}
                            </TableBody>
                        </table>

                        {allocations === undefined || (error && isValidating) ? (
                            <Loading />
                        ) : length < 1 ? (
                            <NoItems />
                        ) : null}
                    </div>
                </Pagination>
            </ContentWrapper>
        </AdminTable>
    );
}

export default (props: Props) => {
    const hooks = useTableHooks<Filters>(props.filters);

    return (
        <AllocationsContext.Provider value={hooks}>
            <AllocationsTable {...props} />
        </AllocationsContext.Provider>
    );
};