import AddOutlined from "@mui/icons-material/AddOutlined";
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
import FileUploadOutlined from "@mui/icons-material/FileUploadOutlined";
import CircularProgress from "@mui/material/CircularProgress";
import InputAdornment from "@mui/material/InputAdornment";
import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField";
import { useEffect, useMemo, useRef, useState } from "react";
import {
    adapterCatalogRows,
    adapterCommonFields,
    adapterConfigFieldValue,
    adapterDefinition,
    formatAdapterConfigJson,
    readAdapterConfig,
    updateAdapterCatalog,
    updateAdapterConfigField,
} from "@/features/games/adapterConfigModel.js";
import { uploadImage } from "@/shared/api/upload";
import { Button } from "@/shared/ui/Button.jsx";
import { JsonEditorField } from "@/shared/ui/JsonEditorField.jsx";
import { useToast } from "@/shared/ui/ToastProvider.jsx";
import styles from "@/features/games/components/AdapterConfigFields.module.css";

const catalogImageAccept = "image/jpeg,image/png,image/webp,.jpg,.jpeg,.png,.webp";

export function AdapterConfigFields({
    adapterType,
    disabled = false,
    onChange,
    onUploadingChange,
    uploadDisabled = false,
    value,
}) {
    const definition = adapterDefinition(adapterType);
    const configValid = Boolean(readAdapterConfig(value));
    const visualFieldsDisabled = disabled || !configValid;

    return (
        <div className={styles.root}>
            {!configValid ? <div className={styles.invalid}>请先在高级配置中修正 JSON 格式</div> : null}
            <ConfigFieldGroup
                disabled={visualFieldsDisabled}
                fields={adapterCommonFields()}
                title="会话参数"
                value={value}
                onChange={onChange}
            />
            {(definition.fields || []).length > 0 ? (
                <ConfigFieldGroup
                    disabled={visualFieldsDisabled}
                    fields={definition.fields}
                    title="厂商参数"
                    value={value}
                    onChange={onChange}
                />
            ) : null}
            {definition.catalog ? (
                <GameCatalogEditor
                    adapterType={adapterType}
                    disabled={visualFieldsDisabled}
                    uploadDisabled={uploadDisabled}
                    value={value}
                    onChange={onChange}
                    onUploadingChange={onUploadingChange}
                />
            ) : null}
            {(definition.advancedFields || []).length > 0 ? (
                <details className={styles.details}>
                    <summary>可选厂商参数</summary>
                    <ConfigFieldGrid
                        disabled={visualFieldsDisabled}
                        fields={definition.advancedFields}
                        value={value}
                        onChange={onChange}
                    />
                </details>
            ) : null}
            <details className={styles.details} open={configValid ? undefined : true}>
                <summary>高级配置 · 完整 JSON</summary>
                <div className={styles.advancedEditor}>
                    <JsonEditorField
                        disabled={disabled}
                        formatter={formatAdapterConfigJson}
                        label="完整配置 JSON"
                        minRows={5}
                        value={value}
                        onChange={onChange}
                    />
                </div>
            </details>
        </div>
    );
}

function ConfigFieldGroup({ disabled, fields, title, value, onChange }) {
    return (
        <section className={styles.group}>
            <div className={styles.groupTitle}>{title}</div>
            <ConfigFieldGrid disabled={disabled} fields={fields} value={value} onChange={onChange} />
        </section>
    );
}

function ConfigFieldGrid({ disabled, fields, value, onChange }) {
    return (
        <div className={styles.fieldGrid}>
            {fields.map((field) => (
                <AdapterField
                    disabled={disabled}
                    field={field}
                    key={field.key}
                    value={adapterConfigFieldValue(value, field)}
                    onChange={(inputValue) => onChange(updateAdapterConfigField(value, field, inputValue))}
                />
            ))}
        </div>
    );
}

function AdapterField({ disabled, field, value, onChange }) {
    const className = field.className === "wide" ? styles.wideField : undefined;
    const commonProps = {
        className,
        disabled,
        label: field.label,
        placeholder: field.placeholder,
        required: field.required,
        value,
        onChange: (event) => onChange(event.target.value),
    };

    if (field.type === "select" || field.type === "selectNumber" || field.type === "selectBoolean") {
        return (
            <TextField {...commonProps} select>
                {(field.options || []).map(([optionValue, optionLabel]) => (
                    <MenuItem key={`${field.key}-${optionValue}`} value={optionValue}>
                        {optionLabel}
                    </MenuItem>
                ))}
            </TextField>
        );
    }

    return (
        <TextField
            {...commonProps}
            slotProps={{
                htmlInput: {
                    min: field.min,
                    step: field.type === "integer" ? 1 : undefined,
                },
                input: field.unit
                    ? { endAdornment: <InputAdornment position="end">{field.unit}</InputAdornment> }
                    : undefined,
            }}
            type={field.type === "integer" ? "number" : "text"}
        />
    );
}

function GameCatalogEditor({ adapterType, disabled, uploadDisabled, value, onChange, onUploadingChange }) {
    const definition = adapterDefinition(adapterType);
    const externalRows = useMemo(() => adapterCatalogRows(value), [value]);
    const externalSignature = useMemo(() => catalogSignature(externalRows), [externalRows]);
    const lastEmittedSignature = useRef(externalSignature);
    const adapterTypeRef = useRef(adapterType);
    const rowSequence = useRef(externalRows.length);
    const [rows, setRows] = useState(() => withRowKeys(externalRows, "initial"));
    const rowsRef = useRef(rows);
    const valueRef = useRef(value);
    const onChangeRef = useRef(onChange);
    const [uploadingAssets, setUploadingAssets] = useState(() => new Set());

    useEffect(() => {
        valueRef.current = value;
        onChangeRef.current = onChange;
    }, [onChange, value]);

    useEffect(() => {
        onUploadingChange?.(uploadingAssets.size > 0);
    }, [onUploadingChange, uploadingAssets]);

    useEffect(() => {
        return () => onUploadingChange?.(false);
    }, [onUploadingChange]);

    useEffect(() => {
        const adapterChanged = adapterTypeRef.current !== adapterType;
        if (adapterChanged || externalSignature !== lastEmittedSignature.current) {
            rowSequence.current += externalRows.length;
            const nextRows = withRowKeys(externalRows, `sync-${rowSequence.current}`);
            rowsRef.current = nextRows;
            setRows(nextRows);
            if (adapterChanged) {
                // 切换厂商会卸载原素材输入；清空聚合状态，迟到的上传结果由字段自身的代次保护丢弃。
                setUploadingAssets(new Set());
            }
        }
        adapterTypeRef.current = adapterType;
        lastEmittedSignature.current = externalSignature;
    }, [adapterType, externalRows, externalSignature]);

    const emitRows = (nextRows) => {
        // 上传完成属于异步回调，必须基于最新 rows/config patch，避免并发上传或期间编辑被旧闭包覆盖。
        rowsRef.current = nextRows;
        setRows(nextRows);
        const nextValue = updateAdapterCatalog(valueRef.current, nextRows);
        lastEmittedSignature.current = catalogSignature(adapterCatalogRows(nextValue));
        onChangeRef.current(nextValue);
    };

    const addRow = () => {
        rowSequence.current += 1;
        const nextRows = [
            ...rowsRef.current,
            {
                rowKey: `draft-${rowSequence.current}`,
                providerGameId: "",
                gameName: "",
                gameUrl: "",
                iconUrl: "",
                coverUrl: "",
                gameLevelUid: "",
            },
        ];
        rowsRef.current = nextRows;
        setRows(nextRows);
    };

    const changeRow = (rowKey, patch) => {
        emitRows(rowsRef.current.map((row) => (row.rowKey === rowKey ? { ...row, ...patch } : row)));
    };

    const removeRow = (rowKey) => {
        emitRows(rowsRef.current.filter((row) => row.rowKey !== rowKey));
    };

    const changeUploadingState = (assetKey, uploading) => {
        setUploadingAssets((current) => {
            const next = new Set(current);
            if (uploading) {
                next.add(assetKey);
            } else {
                next.delete(assetKey);
            }
            return next;
        });
    };

    const assetsUploading = uploadingAssets.size > 0;

    return (
        <section className={styles.catalog}>
            <div className={styles.catalogHeader}>
                <div className={styles.groupTitle}>游戏目录 · {rows.length}</div>
                <Button disabled={disabled || assetsUploading} onClick={addRow}>
                    <AddOutlined fontSize="small" />
                    添加游戏
                </Button>
            </div>
            {rows.length > 0 ? (
                <div className={styles.catalogRows}>
                    {rows.map((row, index) => {
                        const duplicate =
                            row.providerGameId.trim() !== "" &&
                            rows.some(
                                (candidate, candidateIndex) =>
                                    candidateIndex !== index &&
                                    candidate.providerGameId.trim() === row.providerGameId.trim(),
                            );
                        return (
                            <div className={styles.catalogRow} key={row.rowKey}>
                                <div className={styles.catalogMain}>
                                    <TextField
                                        disabled={disabled}
                                        error={duplicate}
                                        helperText={duplicate ? "游戏 ID 重复" : undefined}
                                        label={definition.numericGameID ? "游戏 ID（正整数）" : "游戏 ID"}
                                        required
                                        slotProps={{
                                            // 重复 ID 无法同时写进 JSON 对象；用原生 pattern 阻止提交，并保留当前行供用户修正。
                                            htmlInput: {
                                                inputMode: definition.numericGameID ? "numeric" : undefined,
                                                pattern: duplicate
                                                    ? "a^"
                                                    : definition.numericGameID
                                                      ? "[1-9][0-9]*"
                                                      : undefined,
                                            },
                                        }}
                                        value={row.providerGameId}
                                        onChange={(event) =>
                                            changeRow(row.rowKey, { providerGameId: event.target.value })
                                        }
                                    />
                                    <TextField
                                        disabled={disabled}
                                        label="游戏名称"
                                        required={definition.catalogPrimary === "name"}
                                        value={row.gameName}
                                        onChange={(event) => changeRow(row.rowKey, { gameName: event.target.value })}
                                    />
                                    <TextField
                                        disabled={disabled}
                                        label="H5 URL"
                                        required={definition.catalogPrimary !== "name"}
                                        value={row.gameUrl}
                                        onChange={(event) => changeRow(row.rowKey, { gameUrl: event.target.value })}
                                    />
                                    <Button
                                        aria-label={`删除第 ${index + 1} 个游戏`}
                                        disabled={disabled || assetsUploading}
                                        onClick={() => removeRow(row.rowKey)}
                                    >
                                        <DeleteOutlineOutlined fontSize="small" />
                                    </Button>
                                </div>
                                <details className={styles.rowDetails}>
                                    <summary>素材与游戏参数</summary>
                                    <div className={styles.rowExtra}>
                                        <CatalogImageURLField
                                            disabled={disabled}
                                            label="图标 URL"
                                            uploadDisabled={uploadDisabled}
                                            value={row.iconUrl}
                                            onChange={(iconUrl) => changeRow(row.rowKey, { iconUrl })}
                                            onUploadingChange={(uploading) =>
                                                changeUploadingState(`${row.rowKey}:icon`, uploading)
                                            }
                                        />
                                        <CatalogImageURLField
                                            disabled={disabled}
                                            label="封面 URL"
                                            uploadDisabled={uploadDisabled}
                                            value={row.coverUrl}
                                            onChange={(coverUrl) => changeRow(row.rowKey, { coverUrl })}
                                            onUploadingChange={(uploading) =>
                                                changeUploadingState(`${row.rowKey}:cover`, uploading)
                                            }
                                        />
                                        {adapterType === "yomi_v4" ? (
                                            <TextField
                                                disabled={disabled}
                                                label="场次 ID"
                                                slotProps={{ htmlInput: { min: 1, step: 1 } }}
                                                type="number"
                                                value={row.gameLevelUid}
                                                onChange={(event) =>
                                                    changeRow(row.rowKey, { gameLevelUid: event.target.value })
                                                }
                                            />
                                        ) : null}
                                    </div>
                                </details>
                            </div>
                        );
                    })}
                </div>
            ) : (
                <div className={styles.empty}>当前无游戏目录</div>
            )}
        </section>
    );
}

function CatalogImageURLField({ disabled, label, onChange, onUploadingChange, uploadDisabled, value }) {
    const inputRef = useRef(null);
    const uploadGenerationRef = useRef(0);
    const onUploadingChangeRef = useRef(onUploadingChange);
    const [uploading, setUploading] = useState(false);
    const { showToast } = useToast();

    useEffect(() => {
        onUploadingChangeRef.current = onUploadingChange;
    }, [onUploadingChange]);

    useEffect(() => {
        return () => {
            // 网络请求不能中途取消；字段卸载后作废结果，防止回写已经切换的厂商或已删除行。
            uploadGenerationRef.current += 1;
            onUploadingChangeRef.current?.(false);
        };
    }, []);

    const openPicker = () => {
        if (!disabled && !uploadDisabled && !uploading) {
            inputRef.current?.click();
        }
    };

    const handleFileChange = async (event) => {
        const file = event.target.files?.[0];
        event.target.value = "";
        if (!file) {
            return;
        }
        const uploadGeneration = ++uploadGenerationRef.current;
        setUploading(true);
        onUploadingChange?.(true);
        try {
            const result = await uploadImage(file);
            if (uploadGeneration !== uploadGenerationRef.current) {
                return;
            }
            onChange(result.url);
            showToast(`${label.replace(/ URL$/, "")}上传成功`, "success");
        } catch (err) {
            if (uploadGeneration !== uploadGenerationRef.current) {
                return;
            }
            // 上传失败只提示，不清空用户原先手填或已保存的 URL。
            showToast(err.message || "图片上传失败", "error");
        } finally {
            if (uploadGeneration === uploadGenerationRef.current) {
                setUploading(false);
                onUploadingChange?.(false);
            }
        }
    };

    return (
        <div className={styles.imageURLField}>
            <TextField
                disabled={disabled || uploading}
                label={label}
                placeholder="粘贴图片 URL，或点击上传"
                slotProps={{
                    // 历史厂商配置允许相对路径和协议相对地址；保留文本校验，只用 inputMode 提供 URL 输入体验。
                    htmlInput: { inputMode: "url" },
                    input: {
                        endAdornment: (
                            <InputAdornment position="end">
                                <button
                                    aria-label={`上传${label.replace(/ URL$/, "")}`}
                                    className={styles.uploadButton}
                                    disabled={disabled || uploadDisabled || uploading}
                                    type="button"
                                    onClick={openPicker}
                                >
                                    {uploading ? (
                                        <CircularProgress color="inherit" size={14} />
                                    ) : (
                                        <FileUploadOutlined fontSize="small" />
                                    )}
                                    上传
                                </button>
                            </InputAdornment>
                        ),
                    },
                }}
                type="text"
                value={value}
                onChange={(event) => onChange(event.target.value)}
            />
            <input
                ref={inputRef}
                accept={catalogImageAccept}
                className={styles.fileInput}
                type="file"
                onChange={handleFileChange}
            />
        </div>
    );
}

function withRowKeys(rows, prefix) {
    return rows.map((row, index) => ({ ...row, rowKey: `${prefix}-${index}` }));
}

function catalogSignature(rows) {
    return JSON.stringify(
        (rows || []).map(({ providerGameId, gameName, gameUrl, iconUrl, coverUrl, gameLevelUid }) => ({
            providerGameId,
            gameName,
            gameUrl,
            iconUrl,
            coverUrl,
            gameLevelUid,
        })),
    );
}
