import { Box, Text, useInput } from 'ink';
import { useState } from 'react';
import { DEFAULT_OUTPUT_FILE_NAME } from '../constants.js';

export interface MultiSelectItem {
    id: string;
    schemaUrl: string;
    outputFile?: string;
}

export interface MultiSelectProps {
    items: MultiSelectItem[];
    onSubmit: (selectedIds: string[]) => void;
}

// Row 0 is a synthetic "select all" toggle; rows 1..N map to items[row - 1].
const SELECT_ALL_ROW = 0;

export function MultiSelect(props: MultiSelectProps) {
    const rowCount = props.items.length + 1;
    const [cursorIndex, setCursorIndex] = useState(SELECT_ALL_ROW);
    const [selectedIds, setSelectedIds] = useState<Set<string>>(() => new Set());

    const allSelected = props.items.length > 0 && selectedIds.size === props.items.length;

    const toggleAll = () =>
        setSelectedIds((previousSelection) =>
            previousSelection.size === props.items.length
                ? new Set()
                : new Set(props.items.map((item) => item.id))
        );

    const toggleOne = (itemId: string) =>
        setSelectedIds((previousSelection) => {
            const nextSelection = new Set(previousSelection);
            if (nextSelection.has(itemId)) nextSelection.delete(itemId);
            else nextSelection.add(itemId);
            return nextSelection;
        });

    useInput((input, key) => {
        if (key.upArrow) {
            // Wrap around to the bottom when moving up from the first row.
            setCursorIndex((current) => (current === 0 ? rowCount - 1 : current - 1));
            return;
        }

        if (key.downArrow) {
            // Wrap around to the top when moving down past the last row.
            setCursorIndex((current) => (current === rowCount - 1 ? 0 : current + 1));
            return;
        }

        if (input === ' ') {
            if (cursorIndex === SELECT_ALL_ROW) {
                toggleAll();
                return;
            }
            const itemId = props.items[cursorIndex - 1]?.id;
            if (itemId !== undefined) toggleOne(itemId);
            return;
        }

        if (input === 'a' || input === 'A') {
            toggleAll();
            return;
        }

        if (key.return) {
            if (selectedIds.size === 0) return;
            // Return ids in the order they appear in the list, not toggle order.
            const orderedIds = props.items
                .map((item) => item.id)
                .filter((id) => selectedIds.has(id));
            props.onSubmit(orderedIds);
        }
    });

    const selectAllActive = cursorIndex === SELECT_ALL_ROW;

    return (
        <Box flexDirection="column">
            <Box>
                <Text color="cyan">{selectAllActive ? '❯' : ' '} </Text>
                <Text color={allSelected ? 'green' : 'gray'}>{allSelected ? '◉' : '◯'} </Text>
                <Text color={selectAllActive ? 'cyan' : undefined}>Select all</Text>
            </Box>
            {props.items.map((item, i) => {
                const isActive = cursorIndex === i + 1;
                const isSelected = selectedIds.has(item.id);
                const cursorChar = isActive ? '❯' : ' ';
                const toggleChar = isSelected ? '◉' : '◯';
                const toggleColor = isSelected ? 'green' : 'gray';
                const schemaColor = isActive ? 'cyan' : undefined;
                const outputLabel = item.outputFile ?? DEFAULT_OUTPUT_FILE_NAME;

                return (
                    <Box key={item.id} flexDirection="column">
                        <Box>
                            <Text color="cyan">{cursorChar} </Text>
                            <Text color={toggleColor}>{toggleChar} </Text>
                            <Text color={schemaColor}>{item.schemaUrl}</Text>
                        </Box>
                        <Box marginLeft={4}>
                            <Text color="gray">→ {outputLabel}</Text>
                        </Box>
                    </Box>
                );
            })}
            <Box marginTop={1}>
                <Text color="gray">
                    space toggle · a all · enter confirm · {selectedIds.size} selected
                </Text>
            </Box>
        </Box>
    );
}
