import React, { useCallback, useEffect, useState } from 'react';
import dayjs from 'dayjs';
import { Button, Table, Modal, Form, Input, InputNumber, Select, Tag, TreeSelect, Upload, Spin } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { Plus, Trash2, Upload as UploadIcon } from 'lucide-react';
import { toast } from 'sonner';
import { useTranslation } from 'react-i18next';
import { materialInstructionsApi, InstructionsVO } from '../../../api/material/instructions';
import { materialTypeApi } from '../../../api/material/type';
import { dictDataApi } from '../../../api/DictData';
import { fileApi } from '../../../api/file';
import {
  pickRecords,
  pickTotal,
  buildPageParams,
  fetchAllMaterialTypes,
  buildMaterialTypeTreeData,
  type MaterialTypeTreeNode,
} from '../materialListUtils';
import { setDictOptions, getDictLabel, type DictDataType } from '../../../utils/dict';
import {
  MaterialPageRoot,
  MaterialToolbarCard,
  MaterialTableCard,
  MaterialPaginationBar,
  MaterialEditButton,
  MaterialDeleteButton,
  MaterialViewButton,
  materialConfirmDelete,
  getMaterialFormModalProps,
  materialFormLayout,
} from '../MaterialPageLayout';

/** 与字典缓存 key 一致，便于 getDictLabel */
const INSTRUCTIONS_FILE_TYPE_DICT = 'materials_instructions_file_type';

function firstDefined(row: Record<string, any>, keys: string[]) {
  for (const k of keys) {
    const v = row[k];
    if (v !== undefined && v !== null && v !== '') return v;
  }
  return undefined;
}

function formatAddTime(row: any): string {
  const v = firstDefined(row, ['createTime', 'create_time', 'addTime', 'add_time', 'gmtCreate', 'gmt_create']);
  if (v === undefined || v === null || v === '') return '—';
  const n = typeof v === 'number' ? v : /^\d+$/.test(String(v).trim()) ? Number(String(v).trim()) : NaN;
  const d = Number.isFinite(n) && n > 1e12 ? dayjs(n) : dayjs(v as string | number | Date);
  return d.isValid() ? d.format('YYYY-MM-DD HH:mm:ss') : String(v);
}

function labelForFileType(raw: unknown): string {
  if (raw === undefined || raw === null || raw === '') return '—';
  const s = String(raw).trim();
  const lower = s.toLowerCase();
  const fromDict = getDictLabel(INSTRUCTIONS_FILE_TYPE_DICT, lower) || getDictLabel(INSTRUCTIONS_FILE_TYPE_DICT, s);
  if (fromDict) return fromDict;
  return s.toUpperCase();
}

async function loadInstructionsFileTypeDict(): Promise<{ label: string; value: string }[]> {
  const dictTypes = [
    'materials_instructions_file_type',
    'instructions_file_type',
    'materials_file_type',
    'iscs_materials_file_type',
    'file_type',
  ];
  for (const dictType of dictTypes) {
    try {
      const res = await dictDataApi.getDictDataPage({ pageNo: 1, pageSize: -1, dictType });
      const rows = pickRecords(res);
      if (!rows?.length) continue;
      const mapped: DictDataType[] = rows.map((r: any) => ({
        dictType: INSTRUCTIONS_FILE_TYPE_DICT,
        label: String(r.label ?? r.dictLabel ?? r.value ?? ''),
        value: String(r.value ?? r.dictValue ?? '').toLowerCase(),
        colorType: 'primary',
        cssClass: '',
      }));
      setDictOptions(INSTRUCTIONS_FILE_TYPE_DICT, mapped);
      return mapped.map((m) => ({ label: m.label, value: String(m.value) }));
    } catch {
      /* try next dictType */
    }
  }
  const fallback: DictDataType[] = [
    { dictType: INSTRUCTIONS_FILE_TYPE_DICT, label: 'PDF', value: 'pdf', colorType: 'primary', cssClass: '' },
    { dictType: INSTRUCTIONS_FILE_TYPE_DICT, label: 'MP4', value: 'mp4', colorType: 'primary', cssClass: '' },
  ];
  setDictOptions(INSTRUCTIONS_FILE_TYPE_DICT, fallback);
  return fallback.map((m) => ({ label: m.label, value: String(m.value) }));
}

type FilePreviewKind = 'video' | 'pdf' | 'image' | 'other';

function resolvePreviewKind(fileType: string | undefined, url: string): FilePreviewKind {
  const u = url.trim().toLowerCase();
  const t = (fileType || '').toLowerCase();
  if (t === 'mp4' || t === 'webm' || t === 'ogg' || /\.(mp4|webm|ogg)(\?|#|$)/i.test(u)) return 'video';
  if (t === 'pdf' || u.includes('.pdf')) return 'pdf';
  if (/\.(png|jpe?g|gif|webp|bmp|svg)(\?|#|$)/i.test(u)) return 'image';
  return 'other';
}

const INSTRUCTIONS_FILE_MAX_MB = 100;
const INSTRUCTIONS_FILE_MAX_BYTES = INSTRUCTIONS_FILE_MAX_MB * 1024 * 1024;

function instructionsFileAccept(file: File): boolean {
  const name = file.name.toLowerCase();
  return name.endsWith('.pdf') || name.endsWith('.mp4');
}

/** 与旧版 UploadFile 一致：选取文件上传，mp4/pdf、100MB；已有地址则预览 + 删除 */
function InstructionsFileUrlControl({
  value,
  onChange,
  fileType,
}: {
  value?: string;
  onChange?: (v: string) => void;
  fileType?: string;
}) {
  const url = (value ?? '').trim();
  if (!url) {
    return (
      <div className="space-y-1">
        <Upload
          accept=".pdf,.mp4,application/pdf,video/mp4"
          maxCount={1}
          showUploadList={false}
          beforeUpload={(file) => {
            if (!instructionsFileAccept(file)) {
              toast.error('仅支持上传 mp4 或 pdf 文件');
              return Upload.LIST_IGNORE;
            }
            if (file.size > INSTRUCTIONS_FILE_MAX_BYTES) {
              toast.error(`文件大小不能超过 ${INSTRUCTIONS_FILE_MAX_MB}MB`);
              return Upload.LIST_IGNORE;
            }
            return true;
          }}
          customRequest={async ({ file, onError, onSuccess }) => {
            try {
              const fileUrl = await fileApi.upload(file as File);
              onChange?.(fileUrl);
              onSuccess?.(fileUrl as any);
            } catch (err: any) {
              toast.error(err?.message || '上传失败');
              onError?.(err);
            }
          }}
        >
          <Button type="primary" icon={<UploadIcon className="h-4 w-4" />}>
            选取文件
          </Button>
        </Upload>
        <div className="text-sm text-red-500">大小不超过 {INSTRUCTIONS_FILE_MAX_MB}MB</div>
        <div className="text-sm text-red-500">格式为 mp4/pdf 的文件</div>
      </div>
    );
  }
  const kind = resolvePreviewKind(fileType, url);
  return (
    <div className="space-y-3">
      <div className="rounded-lg border border-gray-200 bg-gray-50/80 p-2 overflow-hidden">
        {kind === 'video' ? (
          <video
            key={url}
            src={url}
            controls
            className="mx-auto block max-h-80 w-full max-w-full rounded bg-black"
          />
        ) : null}
        {kind === 'pdf' ? (
          <iframe title="pdf-preview" src={url} className="h-80 w-full rounded border-0 bg-white" />
        ) : null}
        {kind === 'image' ? (
          <img
            alt=""
            src={url}
            className="mx-auto block max-h-80 max-w-full rounded object-contain"
          />
        ) : null}
        {kind === 'other' ? (
          <div className="flex flex-col items-center justify-center gap-2 py-8 text-center text-sm text-gray-600">
            <span>当前文件类型不支持内嵌预览</span>
            <Button type="link" className="p-0 h-auto" onClick={() => window.open(url, '_blank', 'noopener,noreferrer')}>
              在新窗口打开
            </Button>
          </div>
        ) : null}
      </div>
      <div>
        <Button danger onClick={() => onChange?.('')}>
          删除
        </Button>
      </div>
    </div>
  );
}

export default function MaterialInstructionsPage() {
  const { t } = useTranslation();
  const [loading, setLoading] = useState(false);
  const [list, setList] = useState<any[]>([]);
  const [total, setTotal] = useState(0);
  const [pageNo, setPageNo] = useState(1);
  const [pageSize, setPageSize] = useState(10);
  const [draftTitle, setDraftTitle] = useState('');
  const [appliedTitle, setAppliedTitle] = useState('');
  const [draftMaterialsTypeId, setDraftMaterialsTypeId] = useState<number | undefined>();
  const [appliedMaterialsTypeId, setAppliedMaterialsTypeId] = useState<number | undefined>();
  const [draftFileType, setDraftFileType] = useState<string | undefined>();
  const [appliedFileType, setAppliedFileType] = useState<string | undefined>();
  const [modalOpen, setModalOpen] = useState(false);
  const [form] = Form.useForm();
  const modalFileType = Form.useWatch('fileType', form);
  const [editing, setEditing] = useState<any | null>(null);
  const [detailLoading, setDetailLoading] = useState(false);
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  const [typeTreeData, setTypeTreeData] = useState<MaterialTypeTreeNode[]>([]);
  const [fileTypeSelectOptions, setFileTypeSelectOptions] = useState<{ label: string; value: string }[]>([]);

  const fetchList = useCallback(async () => {
    setLoading(true);
    try {
      const res = await materialInstructionsApi.listInstructions({
        ...buildPageParams(pageNo, pageSize),
        instructionsTitle: appliedTitle || undefined,
        materialsTypeId: appliedMaterialsTypeId,
        fileType: appliedFileType || undefined,
      });
      setList(pickRecords(res));
      setTotal(pickTotal(res));
    } catch (e: any) {
      toast.error(e?.message || t('common.operationFailed'));
    } finally {
      setLoading(false);
    }
  }, [pageNo, pageSize, appliedTitle, appliedMaterialsTypeId, appliedFileType, t]);

  useEffect(() => {
    fetchList();
  }, [fetchList]);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const flat = await fetchAllMaterialTypes(materialTypeApi.listType, {});
        if (cancelled) return;
        setTypeTreeData(buildMaterialTypeTreeData(Array.isArray(flat) ? flat : []));
      } catch {
        if (!cancelled) setTypeTreeData([]);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    void (async () => {
      const opts = await loadInstructionsFileTypeDict();
      const allow = new Set(['pdf', 'mp4']);
      const filtered = opts.filter((o) => allow.has(String(o.value).toLowerCase()));
      setFileTypeSelectOptions(
        filtered.length > 0
          ? filtered
          : [
              { label: 'PDF', value: 'pdf' },
              { label: 'MP4', value: 'mp4' },
            ],
      );
    })();
  }, []);

  const openForm = async (row?: any) => {
    setEditing(null);
    form.resetFields();
    setModalOpen(true);
    if (!row) {
      form.setFieldsValue({ orderNum: 0 });
      return;
    }
    const id = Number(row.instructionsId ?? row.id);
    if (!Number.isFinite(id) || id <= 0) {
      toast.error('无效的说明编号');
      setModalOpen(false);
      return;
    }
    setDetailLoading(true);
    try {
      const detail = await materialInstructionsApi.getInstructionsInfo(id);
      const d = detail && typeof detail === 'object' ? detail : row;
      setEditing(d);
      const ft = firstDefined(d, ['fileType', 'file_type']);
      const sortRaw = firstDefined(d, ['orderNum', 'sort', 'sortOrder', 'orders']);
      const sortNum = sortRaw !== undefined && sortRaw !== null && sortRaw !== '' ? Number(sortRaw) : NaN;
      form.setFieldsValue({
        instructionsTitle: firstDefined(d, ['instructionsTitle', 'instructions_title', 'title']),
        materialsTypeId: firstDefined(d, ['materialsTypeId', 'materials_type_id', 'typeId']),
        fileType: ft != null ? String(ft).toLowerCase() : undefined,
        fileUrl: firstDefined(d, ['fileUrl', 'file_url', 'url']) ?? '',
        orderNum: Number.isFinite(sortNum) ? sortNum : 0,
      });
    } catch (e: any) {
      toast.error(e?.message || t('common.operationFailed'));
      setModalOpen(false);
    } finally {
      setDetailLoading(false);
    }
  };

  const submit = async () => {
    const v = await form.validateFields();
    setLoading(true);
    try {
      const fileTypeNorm = String(v.fileType || '').toLowerCase();
      const parsedOrder =
        v.orderNum === undefined || v.orderNum === null || v.orderNum === ''
          ? NaN
          : Number(v.orderNum);
      const orderNumOut = Number.isFinite(parsedOrder) ? parsedOrder : 0;
      const fileUrlStr = v.fileUrl != null && String(v.fileUrl).trim() !== '' ? String(v.fileUrl).trim() : '';
      const payload: InstructionsVO = {
        instructionsTitle: v.instructionsTitle,
        materialsTypeId: v.materialsTypeId,
        fileType: fileTypeNorm,
        fileUrl: fileUrlStr,
        orderNum: orderNumOut,
      };
      if (editing) {
        await materialInstructionsApi.updateInstructions({
          ...editing,
          ...payload,
          instructionsId: editing.instructionsId ?? editing.id,
        } as InstructionsVO);
        toast.success(t('common.updateSuccess'));
      } else {
        await materialInstructionsApi.addInstructions(payload);
        toast.success(t('common.addSuccess'));
      }
      setModalOpen(false);
      fetchList();
    } catch (e: any) {
      if (!e?.errorFields) toast.error(e?.message || t('common.operationFailed'));
    } finally {
      setLoading(false);
    }
  };

  const onDelete = (row: any) => {
    const id = row.instructionsId ?? row.id;
    materialConfirmDelete(t, {
      count: 1,
      onOk: async () => {
        await materialInstructionsApi.delInstructions(id);
        toast.success(t('common.deleteSuccess'));
        setSelectedRowKeys((prev) => prev.filter((k) => String(k) !== String(id)));
        fetchList();
      },
    });
  };

  const batchDelete = () => {
    if (selectedRowKeys.length === 0) {
      toast.error(t('common.pleaseSelectDataToDelete'));
      return;
    }
    const ids = selectedRowKeys
      .map((k) => Number(k))
      .filter((n) => Number.isFinite(n) && !Number.isNaN(n));
    if (ids.length === 0) {
      toast.error(t('common.pleaseSelectDataToDelete'));
      return;
    }
    materialConfirmDelete(t, {
      count: ids.length,
      onOk: async () => {
        await materialInstructionsApi.delInstructions(ids.join(','));
        toast.success(t('common.deleteSuccess'));
        setSelectedRowKeys([]);
        fetchList();
      },
    });
  };

  const columns: ColumnsType<any> = [
    {
      title: '说明编号',
      key: 'instructionsId',
      width: 96,
      align: 'center',
      render: (_, row) => String(firstDefined(row, ['instructionsId', 'instructions_id', 'id']) ?? '—'),
    },
    {
      title: '排序',
      key: 'sort',
      width: 72,
      align: 'center',
      render: (_, row) => String(firstDefined(row, ['sort', 'sortOrder', 'orderNum', 'orders']) ?? '0'),
    },
    {
      title: '标题',
      key: 'instructionsTitle',
      width: 200,
      ellipsis: true,
      align: 'center',
      render: (_, row) =>
        String(firstDefined(row, ['instructionsTitle', 'instructions_title', 'title']) ?? '—'),
    },
    {
      title: '物资类型',
      key: 'materialsTypeName',
      width: 160,
      ellipsis: true,
      align: 'center',
      render: (_, row) =>
        String(
          firstDefined(row, [
            'materialsTypeName',
            'materials_type_name',
            'typeName',
            'type_name',
            'materialsType',
          ]) ?? '—',
        ),
    },
    {
      title: '类型',
      key: 'fileType',
      width: 112,
      align: 'center',
      render: (_, row) => {
        const raw = firstDefined(row, ['fileType', 'file_type', 'instructionsFileType']);
        const label = labelForFileType(raw);
        return <Tag color="blue">{label}</Tag>;
      },
    },
    {
      title: '文件',
      key: 'fileUrl',
      width: 88,
      align: 'center',
      render: (_, row) => {
        const url = firstDefined(row, ['fileUrl', 'file_url', 'url']);
        if (!url) return <span className="text-gray-400">—</span>;
        return (
          <MaterialViewButton
            label="查看"
            onClick={() => window.open(String(url), '_blank', 'noopener,noreferrer')}
          />
        );
      },
    },
    {
      title: '添加时间',
      key: 'createTime',
      width: 176,
      align: 'center',
      render: (_, row) => formatAddTime(row),
    },
    {
      title: t('table.operation'),
      width: 148,
      align: 'center',
      fixed: 'right',
      render: (_, row) => (
        <div className="flex items-center gap-2 justify-center">
          <MaterialEditButton label="修改" onClick={() => openForm(row)} />
          <MaterialDeleteButton label={t('common.delete')} onClick={() => onDelete(row)} />
        </div>
      ),
    },
  ];

  const handleSearch = () => {
    setAppliedTitle(draftTitle);
    setAppliedMaterialsTypeId(draftMaterialsTypeId);
    setAppliedFileType(draftFileType);
    setPageNo(1);
    setSelectedRowKeys([]);
  };

  const handleReset = () => {
    setDraftTitle('');
    setAppliedTitle('');
    setDraftMaterialsTypeId(undefined);
    setAppliedMaterialsTypeId(undefined);
    setDraftFileType(undefined);
    setAppliedFileType(undefined);
    setPageNo(1);
    setSelectedRowKeys([]);
  };

  return (
    <MaterialPageRoot>
      <MaterialToolbarCard
        filterRow={
          <div className="flex flex-wrap items-center gap-3">
            <div className="flex items-center gap-2">
              <span className="text-sm font-medium text-gray-700 whitespace-nowrap">标题：</span>
              <Input
                allowClear
                placeholder="请输入标题"
                className="w-[180px]"
                value={draftTitle}
                onChange={(e) => setDraftTitle(e.target.value)}
                onPressEnter={handleSearch}
              />
            </div>
            <div className="flex items-center gap-2">
              <span className="text-sm font-medium text-gray-700 whitespace-nowrap">物资类型：</span>
              <TreeSelect
                allowClear
                showSearch
                treeDefaultExpandAll
                treeLine={{ showLeafIcon: false }}
                treeData={typeTreeData}
                treeNodeFilterProp="title"
                placeholder="请选择物资类型"
                className="min-w-[200px] w-[200px]"
                value={draftMaterialsTypeId}
                onChange={(v) => setDraftMaterialsTypeId(v ?? undefined)}
              />
            </div>
            <div className="flex items-center gap-2">
              <span className="text-sm font-medium text-gray-700 whitespace-nowrap">类型：</span>
              <Select
                allowClear
                showSearch
                optionFilterProp="label"
                placeholder="请选择类型"
                className="w-[160px]"
                options={fileTypeSelectOptions}
                value={draftFileType}
                onChange={(v) => setDraftFileType(v)}
              />
            </div>
          </div>
        }
        onSearch={handleSearch}
        onReset={handleReset}
        toolbarRight={
          <>
            <Button type="primary" icon={<Plus />} onClick={() => openForm()}>
              {t('common.addNew')}
            </Button>
            <Button
              danger
              icon={<Trash2 className="h-4 w-4" />}
              disabled={selectedRowKeys.length === 0}
              onClick={batchDelete}
            >
              {t('common.batchDelete')}
            </Button>
          </>
        }
      />
      <MaterialTableCard>
        <Table
          rowKey={(r) => r.instructionsId ?? r.id}
          loading={loading}
          columns={columns}
          dataSource={list}
          pagination={false}
          scroll={{ x: 1200 }}
          rowSelection={{
            columnWidth: 48,
            selectedRowKeys,
            onChange: setSelectedRowKeys,
            preserveSelectedRowKeys: true,
            getCheckboxProps: (record) => ({ name: record.instructionsTitle }),
          }}
          rowClassName={(_, index) => (index !== undefined && index % 2 === 1 ? 'isolation-work-list-row-alt' : '')}
        />
      </MaterialTableCard>
      <MaterialPaginationBar
        total={total}
        current={pageNo}
        pageSize={pageSize}
        onPageChange={setPageNo}
        loading={loading}
        listLength={list.length}
        onPageSizeChange={(size) => {
          setPageSize(size);
          setPageNo(1);
        }}
        showQuickJumper
      />

      <Modal
        title={editing ? '修改' : '新增'}
        open={modalOpen}
        onOk={submit}
        onCancel={() => {
          setModalOpen(false);
          setDetailLoading(false);
        }}
        confirmLoading={loading}
        {...getMaterialFormModalProps(t, 720)}
      >
        <Spin spinning={detailLoading}>
          <Form form={form} {...materialFormLayout}>
            <Form.Item name="orderNum" label="显示排序">
              <InputNumber min={0} step={1} placeholder="0" className="w-full max-w-xs" />
            </Form.Item>
            <Form.Item name="instructionsTitle" label="标题" rules={[{ required: true, message: '请输入标题' }]}>
              <Input allowClear placeholder="请输入标题" />
            </Form.Item>
            <Form.Item name="materialsTypeId" label="物资类型" rules={[{ required: true, message: '请选择物资类型' }]}>
              <TreeSelect
                showSearch
                treeDefaultExpandAll
                treeLine={{ showLeafIcon: false }}
                treeData={typeTreeData}
                treeNodeFilterProp="title"
                placeholder="请选择物资类型"
                className="w-full max-w-md"
              />
            </Form.Item>
            <Form.Item name="fileType" label="类型" rules={[{ required: true, message: '请选择文件类型' }]}>
              <Select
                showSearch
                optionFilterProp="label"
                placeholder="请选择文件类型"
                options={fileTypeSelectOptions}
              />
            </Form.Item>
            <Form.Item name="fileUrl" label="文件">
              <InstructionsFileUrlControl fileType={modalFileType} />
            </Form.Item>
          </Form>
        </Spin>
      </Modal>
    </MaterialPageRoot>
  );
}
