import React, { useEffect, useMemo, useState } from 'react';
import tw from 'twin.macro';
import { Dialog } from '@/components/elements/dialog';
import { Button } from '@/components/elements/button';
import Input from '@/components/elements/Input';
import Label from '@/components/elements/Label';
import FlashMessageRender from '@/components/FlashMessageRender';
import useFlash from '@/plugins/useFlash';
import { ServerContext } from '@/state/server';
import { httpErrorToHuman } from '@/api/http';
import { insertTableRow, OperationResponse } from '../api/tableDataOperations';
import { DatabaseColumn } from '../api/getDatabaseContents';

interface AddRowDialogContext {
    databaseId: string;
    tableName: string;
    columns: DatabaseColumn[];
}

interface AddRowDialogProps {
    open: boolean;
    onClose: () => void;
    context: AddRowDialogContext;
    onSuccess?: (response: OperationResponse) => void;
}

const FLASH_KEY = 'database:content:add-row-dialog';

const isGeneratedColumn = (column: DatabaseColumn): boolean => {
    return (
        column.extra.toLowerCase().includes('auto_increment') ||
        /^nextval\(.+::regclass\)$/i.test(String(column.column_default || '').trim())
    );
};

const AddRowDialog = ({ open, onClose, context, onSuccess }: AddRowDialogProps) => {
    const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
    const { clearFlashes, addError } = useFlash();

    const [isSubmitting, setIsSubmitting] = useState(false);
    const [values, setValues] = useState<Record<string, string>>({});
    const [customGeneratedValues, setCustomGeneratedValues] = useState<Record<string, boolean>>({});

    const columns = context.columns || [];

    useEffect(() => {
        if (!open) {
            clearFlashes(FLASH_KEY);
            setIsSubmitting(false);
            setValues({});
            setCustomGeneratedValues({});
            return;
        }

        clearFlashes(FLASH_KEY);

        const initialValues = columns.reduce<Record<string, string>>((acc, column) => {
            acc[column.column_name] = '';
            return acc;
        }, {});

        setValues(initialValues);
        setCustomGeneratedValues({});
    }, [open]);

    const requiredColumns = useMemo(
        () =>
            columns.filter(
                (column) =>
                    column.is_nullable !== 'YES' &&
                    !column.column_default &&
                    !isGeneratedColumn(column)
            ),
        [columns]
    );

    const updateValue = (columnName: string, nextValue: string) => {
        setValues((current) => ({ ...current, [columnName]: nextValue }));
    };

    const toggleCustomGeneratedValue = (columnName: string, enabled: boolean) => {
        setCustomGeneratedValues((current) => ({ ...current, [columnName]: enabled }));

        if (!enabled) {
            setValues((current) => ({ ...current, [columnName]: '' }));
        }
    };

    const validate = (): string | null => {
        if (!context.databaseId.trim() || !context.tableName.trim()) {
            return 'Database table context is missing. Close and reopen the dialog.';
        }

        if (columns.length < 1) {
            return 'No table columns are available for row insertion.';
        }

        for (const column of requiredColumns) {
            const rawValue = values[column.column_name];
            if (!rawValue || !rawValue.trim()) {
                return `Column "${column.column_name}" is required.`;
            }
        }

        for (const column of columns) {
            if (!isGeneratedColumn(column) || !customGeneratedValues[column.column_name]) {
                continue;
            }

            const rawValue = values[column.column_name];
            if (!rawValue || !rawValue.trim()) {
                return `Column "${column.column_name}" needs a value or should use auto increment.`;
            }
        }

        const hasAnyProvidedValue = columns.some((column) => {
            const rawValue = values[column.column_name];
            return !!rawValue && rawValue.trim().length > 0;
        });

        if (!hasAnyProvidedValue && requiredColumns.length > 0) {
            return 'Provide at least one column value.';
        }

        return null;
    };

    const submit = async () => {
        clearFlashes(FLASH_KEY);

        const validationError = validate();
        if (validationError) {
            addError({ key: FLASH_KEY, message: validationError });
            return;
        }

        setIsSubmitting(true);

        try {
            const rowData = columns.reduce<Record<string, string>>((acc, column) => {
                const rawValue = values[column.column_name];
                const trimmed = rawValue?.trim() || '';

                if (isGeneratedColumn(column) && !customGeneratedValues[column.column_name]) {
                    return acc;
                }

                if (trimmed) {
                    acc[column.column_name] = trimmed;
                }

                return acc;
            }, {});

            const response = await insertTableRow(uuid, context.databaseId, context.tableName, {
                row_data: rowData,
            });

            if (!response.success) {
                throw new Error(response.error || 'Failed to add row.');
            }

            onSuccess?.(response);
            onClose();
        } catch (error) {
            addError({ key: FLASH_KEY, message: httpErrorToHuman(error) });
        }

        setIsSubmitting(false);
    };

    return (
        <Dialog
            open={open}
            onClose={onClose}
            title={'Add row'}
            description={`Table: ${context.tableName || '-'}`}
            preventExternalClose={isSubmitting}
        >
            <div css={tw`mt-6 space-y-4 max-h-[65vh] overflow-y-auto pr-1`}>
                <FlashMessageRender byKey={FLASH_KEY} css={tw`mb-2`} />

                {columns.length < 1 ? (
                    <p css={tw`text-sm text-neutral-400`}>No columns available for this table.</p>
                ) : (
                    columns.map((column) => {
                        const isAutoIncrement = isGeneratedColumn(column);
                        const usesGeneratedValue = isAutoIncrement && !customGeneratedValues[column.column_name];
                        const isRequired = column.is_nullable !== 'YES' && !column.column_default && !isAutoIncrement;

                        return (
                            <div key={column.column_name}>
                                <Label htmlFor={`add-row-${column.column_name}`}>
                                    {column.column_name}
                                    {isRequired ? ' (required)' : ''}
                                </Label>
                                {isAutoIncrement && (
                                    <div css={tw`mb-2 flex items-center rounded border border-neutral-600 bg-neutral-800 px-3 py-2`}>
                                        <Input
                                            id={`add-row-${column.column_name}-custom`}
                                            type={'checkbox'}
                                            checked={!!customGeneratedValues[column.column_name]}
                                            onChange={(event) =>
                                                toggleCustomGeneratedValue(column.column_name, event.currentTarget.checked)
                                            }
                                            disabled={isSubmitting}
                                        />
                                        <Label htmlFor={`add-row-${column.column_name}-custom`} css={tw`ml-3 mb-0 text-xs`}>
                                            Write custom value instead of auto increment
                                        </Label>
                                    </div>
                                )}
                                <Input
                                    id={`add-row-${column.column_name}`}
                                    value={values[column.column_name] || ''}
                                    placeholder={usesGeneratedValue ? 'Auto increment' : undefined}
                                    onChange={(event) => updateValue(column.column_name, event.currentTarget.value)}
                                    disabled={isSubmitting || usesGeneratedValue}
                                />
                                <p css={tw`text-xs text-neutral-400 mt-1`}>
                                    {column.data_type.toUpperCase()} | nullable: {column.is_nullable} | key:{' '}
                                    {column.column_key || 'NONE'} | auto_increment: {isAutoIncrement ? 'YES' : 'NO'} |
                                    default: {column.column_default === null ? 'NULL' : String(column.column_default)}
                                </p>
                            </div>
                        );
                    })
                )}
            </div>

            <Dialog.Footer>
                <Button type={'button'} variant={Button.Variants.Secondary} onClick={onClose} disabled={isSubmitting}>
                    Cancel
                </Button>
                <Button type={'button'} onClick={submit} disabled={isSubmitting || columns.length < 1}>
                    Add Row
                </Button>
            </Dialog.Footer>
        </Dialog>
    );
};

export default AddRowDialog;
