import * as React from 'react';
import { ApiError } from '@/lib/ApiError';
import AdminLayout from '../layout';
import {
    questionsApi,
    type ImportPreview,
    type ImportResult,
    type QuestionImportMapping,
} from '@/features/admin/questions/services/api'; 
import { Download } from 'lucide-react'
import {
    Card,
    CardContent,
    CardHeader,
    CardTitle,
    CardDescription,
} from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { FileUpload } from '@/components/ui/file-upload';
import { Alert } from '@/components/ui/alert';
import { Select } from '@/components/ui/select';
import {
    Table,
    TableHead,
    TableBody,
    TableRow,
    TableHeaderCell,
    TableCell,
} from '@/components/ui/table';
import { toast } from '@/components/ui/sonner';
import { downloadBlob } from '@/lib/utils';

function suggestedSource(
    fieldKey: string,
    columns: ImportPreview['columns'],
): string | undefined {
    const suggestions: Record<string, string[]> = {
        text: ['question', 'text'],
        option_a: ['option_a'],
        option_b: ['option_b'],
        option_c: ['option_c'],
        option_d: ['option_d'],
        correct_option: ['correct_option'],
        active: ['active'],
    };

    return columns.find((column) => suggestions[fieldKey]?.includes(column.key))
        ?.key;
}

function suggestedMapping(
    fields: ImportPreview['fields'],
    columns: ImportPreview['columns'],
): QuestionImportMapping {
    return Object.fromEntries(
        fields
            .map((field) => [field.key, suggestedSource(field.key, columns)])
            .filter((entry): entry is [string, string] => Boolean(entry[1])),
    );
}

function columnsForSheets(
    preview: ImportPreview,
    selectedSheets: string[],
): ImportPreview['columns'] {
    return (
        preview.sheets.find((sheet) => sheet.name === selectedSheets[0])
            ?.columns ?? preview.columns
    );
}

function headerRowsForPreview(preview: ImportPreview): Record<string, number> {
    return Object.fromEntries(
        preview.sheets
            .filter((sheet) => sheet.header_row !== null)
            .map((sheet) => [sheet.name, sheet.header_row as number]),
    );
}

/** Bulk-import questions by mapping spreadsheet columns to question database fields. */
export default function QuestionsImport() {
    const [file, setFile] = React.useState<File | null>(null);
    const [preview, setPreview] = React.useState<ImportPreview | null>(null);
    const [selectedSheets, setSelectedSheets] = React.useState<string[]>([]);
    const [headerRows, setHeaderRows] = React.useState<Record<string, number>>(
        {},
    );
    const [mapping, setMapping] = React.useState<QuestionImportMapping>({});
    const [previewing, setPreviewing] = React.useState(false);
    const [importing, setImporting] = React.useState(false);
    const [downloadingTemplate, setDownloadingTemplate] = React.useState(false);
    const [result, setResult] = React.useState<ImportResult | null>(null);

    const selectedSheetDetails = preview?.sheets.filter((sheet) =>
        selectedSheets.includes(sheet.name),
    );
    const selectedColumns = preview
        ? columnsForSheets(preview, selectedSheets)
        : [];
    const selectedSheetsAreValid = Boolean(
        selectedSheetDetails?.length &&
        selectedSheetDetails.every((sheet) => !sheet.error),
    );
    const requiredFieldsMapped = Boolean(
        selectedSheetsAreValid &&
        preview?.fields
            .filter((field) => field.required)
            .every((field) => Boolean(mapping[field.key])),
    );

    function handleFileSelected(nextFile: File | null) {
        setFile(nextFile);
        setPreview(null);
        setSelectedSheets([]);
        setHeaderRows({});
        setMapping({});
        setResult(null);
    }

    function updateSelectedSheets(nextSheets: string[]) {
        setSelectedSheets(nextSheets);

        if (preview) {
            setMapping(
                suggestedMapping(
                    preview.fields,
                    columnsForSheets(preview, nextSheets),
                ),
            );
        }
    }

    async function handleHeaderRowChange(sheetName: string, row: number) {
        if (!file || !preview || !Number.isInteger(row) || row < 1) return;

        const nextHeaderRows = { ...headerRows, [sheetName]: row };
        setHeaderRows(nextHeaderRows);
        setPreviewing(true);
        setResult(null);

        try {
            const nextPreview = await questionsApi.preview(
                file,
                nextHeaderRows,
            );
            setPreview(nextPreview);
            setHeaderRows(headerRowsForPreview(nextPreview));
            setMapping(
                suggestedMapping(
                    nextPreview.fields,
                    columnsForSheets(nextPreview, selectedSheets),
                ),
            );
        } catch (error) {
            toast.error(
                error instanceof ApiError
                    ? error.message
                    : 'Could not read the selected header row.',
            );
        } finally {
            setPreviewing(false);
        }
    }

    async function handleDownloadTemplate() {
        setDownloadingTemplate(true);
        try {
            const blob = await questionsApi.template();
            downloadBlob(blob, 'questions-import-template.xlsx');
        } catch {
            toast.error('Failed to download template.');
        } finally {
            setDownloadingTemplate(false);
        }
    }

    async function handlePreview() {
        if (!file) return;
        setPreviewing(true);
        setResult(null);
        try {
            const nextPreview = await questionsApi.preview(file);
            const defaultSheet =
                nextPreview.sheets.find(
                    (sheet) => sheet.active && !sheet.error,
                ) ?? nextPreview.sheets.find((sheet) => !sheet.error);
            const nextSelectedSheets = defaultSheet ? [defaultSheet.name] : [];

            setPreview(nextPreview);
            setHeaderRows(headerRowsForPreview(nextPreview));
            setSelectedSheets(nextSelectedSheets);
            setMapping(
                suggestedMapping(
                    nextPreview.fields,
                    columnsForSheets(nextPreview, nextSelectedSheets),
                ),
            );
        } catch (error) {
            toast.error(
                error instanceof ApiError
                    ? error.message
                    : 'Could not read the spreadsheet headings.',
            );
        } finally {
            setPreviewing(false);
        }
    }

    async function handleImport() {
        if (!file || !preview || !requiredFieldsMapped) return;
        setImporting(true);
        setResult(null);
        try {
            const res = await questionsApi.import(
                file,
                mapping,
                selectedSheets,
                headerRows,
            );
            setResult(res);
            toast.success(
                `Imported ${res.imported} question${res.imported === 1 ? '' : 's'}.`,
            );
        } catch (error) {
            toast.error(
                error instanceof ApiError
                    ? error.message
                    : 'Import failed. Check the mapping and try again.',
            );
        } finally {
            setImporting(false);
        }
    }

    return (
        <AdminLayout title="Import questions">
            <Card className="max-w-3xl">
                <CardHeader>
                    <div>
                        <CardTitle>Bulk import</CardTitle>
                        <CardDescription className='max-w-md'>
                            Upload a spreadsheet, choose worksheets, then map
                            their headings to the question database columns.
                        </CardDescription>
                    </div>
                    <Button
                        variant="outline"
                        size="sm"
                        onClick={handleDownloadTemplate}
                        loading={downloadingTemplate} 
                        className='p-4'
                    >
                        <Download className='h-4' /> Template
                    </Button>
                </CardHeader>
                <CardContent className="space-y-4">
                    <FileUpload
                        accept=".xlsx,.xls,.csv"
                        hint="Accepted formats: .xlsx, .xls, .csv"
                        disabled={importing || previewing}
                        onFileSelected={handleFileSelected}
                    />

                    <Button
                        variant="outline"
                        onClick={handlePreview}
                        disabled={!file}
                        loading={previewing} 
                        className='me-2'
                    >
                        Read spreadsheet headings
                    </Button>

                    {preview && (
                        <div className="space-y-4 rounded-lg border border-(--color-border) p-4">
                            <div>
                                <h3 className="text-sm font-semibold text-(--color-ink)">
                                    Choose worksheets
                                </h3>
                                <p className="text-sm text-(--color-ink-muted)">
                                    The active worksheet is selected by default.
                                    Select multiple worksheets only when they
                                    use compatible headings.
                                </p>
                            </div>
                            <div className="grid gap-2">
                                {preview.sheets.map((sheet) => (
                                    <label
                                        className="flex items-start gap-2 rounded-md border border-(--color-border) p-3 text-sm text-(--color-ink)"
                                        key={sheet.name}
                                    >
                                        <input
                                            type="checkbox"
                                            className="mt-0.5"
                                            checked={selectedSheets.includes(
                                                sheet.name,
                                            )}
                                            disabled={
                                                Boolean(sheet.error) ||
                                                importing
                                            }
                                            onChange={() => {
                                                const nextSheets =
                                                    selectedSheets.includes(
                                                        sheet.name,
                                                    )
                                                        ? selectedSheets.filter(
                                                              (name) =>
                                                                  name !==
                                                                  sheet.name,
                                                          )
                                                        : [
                                                              ...selectedSheets,
                                                              sheet.name,
                                                          ];
                                                updateSelectedSheets(
                                                    nextSheets,
                                                );
                                            }}
                                        />
                                        <span className="grid gap-1">
                                            <span className="font-medium">
                                                {sheet.name}
                                                {sheet.active
                                                    ? ' (active)'
                                                    : ''}
                                            </span>
                                            {sheet.header_candidates.length >
                                                0 && (
                                                <span className="grid gap-1 text-xs font-normal text-(--color-ink-muted)">
                                                    Header row
                                                    <Select
                                                        value={String(
                                                            headerRows[
                                                                sheet.name
                                                            ] ??
                                                                sheet.header_row ??
                                                                '',
                                                        )}
                                                        options={sheet.header_candidates.map(
                                                            (candidate) => ({
                                                                value: String(
                                                                    candidate.row,
                                                                ),
                                                                label: `Row ${candidate.row}`,
                                                            }),
                                                        )}
                                                        onChange={(event) => {
                                                            void handleHeaderRowChange(
                                                                sheet.name,
                                                                Number(
                                                                    event.target
                                                                        .value,
                                                                ),
                                                            );
                                                        }}
                                                        disabled={
                                                            Boolean(
                                                                sheet.error,
                                                            ) ||
                                                            importing ||
                                                            previewing
                                                        }
                                                    />
                                                </span>
                                            )}
                                            {sheet.error && (
                                                <span className="text-(--color-danger)">
                                                    {sheet.error}
                                                </span>
                                            )}
                                        </span>
                                    </label>
                                ))}
                            </div>

                            <div>
                                <h3 className="text-sm font-semibold text-(--color-ink)">
                                    Map spreadsheet columns
                                </h3>
                                <p className="text-sm text-(--color-ink-muted)">
                                    Select the spreadsheet heading for each
                                    database column. Unmapped spreadsheet
                                    columns are ignored.
                                </p>
                            </div>
                            <div className="grid gap-3">
                                {preview.fields.map((field) => (
                                    <label
                                        className="grid gap-1.5 text-sm font-medium text-(--color-ink)"
                                        key={field.key}
                                    >
                                        {field.label}
                                        {field.required ? ' *' : ' (optional)'}
                                        <Select
                                            value={mapping[field.key] ?? ''}
                                            onChange={(event) => {
                                                const value =
                                                    event.target.value;
                                                setMapping((current) => {
                                                    const next = { ...current };
                                                    if (value) {
                                                        next[field.key] = value;
                                                    } else {
                                                        delete next[field.key];
                                                    }
                                                    return next;
                                                });
                                            }}
                                            options={selectedColumns.map(
                                                (column) => ({
                                                    value: column.key,
                                                    label: column.heading,
                                                }),
                                            )}
                                            placeholder={
                                                field.required
                                                    ? 'Select a spreadsheet column'
                                                    : 'Do not import this column'
                                            }
                                            disabled={
                                                importing ||
                                                selectedSheets.length === 0
                                            }
                                        />
                                    </label>
                                ))}
                            </div>
                            {selectedSheets.length === 0 && (
                                <Alert variant="warning">
                                    Select at least one worksheet before
                                    importing.
                                </Alert>
                            )}
                            {selectedSheets.length > 1 && (
                                <Alert variant="info">
                                    All selected worksheets must contain
                                    compatible headings for this mapping.
                                </Alert>
                            )}
                            {selectedSheets.length > 0 &&
                                !requiredFieldsMapped && (
                                    <Alert variant="warning">
                                        Map every required database column
                                        before importing.
                                    </Alert>
                                )}
                        </div>
                    )}

                    <Button
                        onClick={handleImport}
                        disabled={!file || !preview || !requiredFieldsMapped}
                        loading={importing} 
                        className='ms-2'
                    >
                        Import questions
                    </Button>

                    {result && (
                        <div className="space-y-3">
                            <Alert
                                variant={
                                    result.errors.length ? 'warning' : 'success'
                                }
                            >
                                Imported {result.imported}, skipped{' '}
                                {result.skipped}.
                            </Alert>
                            {result.errors.length > 0 && (
                                <Table>
                                    <TableHead>
                                        <tr>
                                            <TableHeaderCell>
                                                Row
                                            </TableHeaderCell>
                                            <TableHeaderCell>
                                                Error
                                            </TableHeaderCell>
                                        </tr>
                                    </TableHead>
                                    <TableBody>
                                        {result.errors.map((error, index) => (
                                            <TableRow key={index}>
                                                <TableCell>
                                                    {error.row}
                                                </TableCell>
                                                <TableCell>
                                                    {error.message}
                                                </TableCell>
                                            </TableRow>
                                        ))}
                                    </TableBody>
                                </Table>
                            )}
                        </div>
                    )}
                </CardContent>
            </Card>
        </AdminLayout>
    );
}
