import { useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
  Alert,
  Button,
  Descriptions,
  Drawer,
  Form,
  Image,
  Input,
  InputNumber,
  Modal,
  Select,
  Space,
  Steps,
  Table,
  Tag,
  Typography,
  message,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { FilePdfOutlined, LinkOutlined, EnvironmentOutlined } from '@ant-design/icons';
import { request, type Paginated } from '../lib/api';
import {
  ADMIN_OPTIONS_PAGE_SIZE,
  RESOURCE_BIZ_TYPE_LABELS,
  STORE_AUDIT_STATUS_LABELS,
  STORE_STATUS_LABELS,
  fmtTime,
} from '../lib/constants';
import {
  validateStoreCreateStep1,
  validateStoreCreateStep3,
  type StoreCreateForm,
} from '../lib/storeCreate';
import { useAdminList } from '../lib/useAdminList';
import { resolveRegionBinding } from '../lib/china-region';
import ChinaRegionCascader from '../components/ChinaRegionCascader';
import OssUpload from '../components/OssUpload';
import TencentLocPickerModal from '../components/TencentLocPickerModal';

const CREATE_STEPS = [
  { title: '基本信息' },
  { title: '照片上传' },
  { title: '结算资质' },
];

function fillGeolocation(
  setCoords: (lat: number, lng: number) => void,
  setLoading: (v: boolean) => void,
) {
  if (!navigator.geolocation) {
    message.error('当前浏览器不支持定位');
    return;
  }
  setLoading(true);
  navigator.geolocation.getCurrentPosition(
    (pos) => {
      setCoords(pos.coords.latitude, pos.coords.longitude);
      message.success(
        `已获取坐标 ${pos.coords.latitude.toFixed(6)}, ${pos.coords.longitude.toFixed(6)}`,
      );
      setLoading(false);
    },
    (err) => {
      message.error(err.message || '定位失败');
      setLoading(false);
    },
    { enableHighAccuracy: true, timeout: 10000 },
  );
}

type StoreMediaItem = {
  id?: string;
  bizType?: string;
  mediaType?: string;
  url?: string | null;
};

function isImageMedia(url: string, mediaType?: string) {
  if (mediaType === 'IMAGE') return true;
  if (mediaType === 'VIDEO' || mediaType === 'FILE') {
    return /\.(png|jpe?g|gif|webp|bmp|heic)(\?|#|$)/i.test(url);
  }
  return /\.(png|jpe?g|gif|webp|bmp|heic)(\?|#|$)/i.test(url);
}

function isPdfUrl(url: string) {
  return /\.pdf(\?|#|$)/i.test(url);
}

function collectMediaUrls(detail: Record<string, unknown>) {
  const media = Array.isArray(detail.media) ? (detail.media as StoreMediaItem[]) : [];
  const byType = (bizType: string) =>
    media
      .filter((item) => String(item.bizType || '').toUpperCase() === bizType)
      .map((item) => ({
        id: String(item.id || item.url || ''),
        url: String(item.url || '').trim(),
        mediaType: item.mediaType ? String(item.mediaType) : undefined,
      }))
      .filter((item) => item.url);

  const covers = byType('COVER');
  const coverUrl = detail.coverUrl ? String(detail.coverUrl).trim() : '';
  if (coverUrl && !covers.some((item) => item.url === coverUrl)) {
    covers.unshift({ id: 'cover', url: coverUrl, mediaType: 'IMAGE' });
  }

  return {
    covers,
    envs: byType('ENV'),
    contracts: byType('CONTRACT'),
  };
}

function StoreAuditMediaSection({ detail }: { detail: Record<string, unknown> }) {
  const { covers, envs, contracts } = collectMediaUrls(detail);
  const [pdfUrl, setPdfUrl] = useState<string | null>(null);
  const gallery = [...covers, ...envs].filter((item) => isImageMedia(item.url, item.mediaType));

  if (covers.length === 0 && envs.length === 0 && contracts.length === 0) {
    return (
      <Alert
        type="warning"
        showIcon
        style={{ marginBottom: 16 }}
        message="暂无门头照 / 环境照 / 签约合同，请谨慎审核"
      />
    );
  }

  return (
    <div style={{ marginBottom: 16 }}>
      <Typography.Title level={5} style={{ marginTop: 0, marginBottom: 12 }}>
        审核材料
      </Typography.Title>

      {(covers.length > 0 || envs.length > 0) && (
        <div style={{ marginBottom: 16 }}>
          <Typography.Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
            门头照 / 环境照（点击可放大浏览）
          </Typography.Text>
          <Image.PreviewGroup>
            <Space wrap size={12}>
              {gallery.map((item) => (
                <div key={item.id} style={{ textAlign: 'center' }}>
                  <Image
                    src={item.url}
                    width={112}
                    height={84}
                    style={{ objectFit: 'cover', borderRadius: 6, border: '1px solid #f0f0f0' }}
                  />
                  <div style={{ fontSize: 12, color: '#8c8c8c', marginTop: 4 }}>
                    {covers.some((c) => c.id === item.id) ? '门头照' : '环境照'}
                  </div>
                </div>
              ))}
            </Space>
          </Image.PreviewGroup>
        </div>
      )}

      {contracts.length > 0 && (
        <div>
          <Typography.Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
            签约合同
          </Typography.Text>
          <Space direction="vertical" size={12} style={{ width: '100%' }}>
            {contracts.map((item, index) => {
              const imageLike = isImageMedia(item.url, item.mediaType);
              const pdf = isPdfUrl(item.url);
              return (
                <div
                  key={item.id || `${item.url}-${index}`}
                  style={{
                    display: 'flex',
                    gap: 12,
                    alignItems: 'center',
                    padding: 12,
                    border: '1px solid #f0f0f0',
                    borderRadius: 8,
                    background: '#fafafa',
                  }}
                >
                  {imageLike ? (
                    <Image.PreviewGroup>
                      <Image
                        src={item.url}
                        width={96}
                        height={72}
                        style={{ objectFit: 'cover', borderRadius: 6 }}
                      />
                    </Image.PreviewGroup>
                  ) : (
                    <div
                      style={{
                        width: 96,
                        height: 72,
                        borderRadius: 6,
                        background: '#fff',
                        border: '1px dashed #d9d9d9',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        color: '#cf1322',
                      }}
                    >
                      <FilePdfOutlined style={{ fontSize: 28 }} />
                    </div>
                  )}
                  <Space direction="vertical" size={4} style={{ flex: 1, minWidth: 0 }}>
                    <Typography.Text strong>
                      {RESOURCE_BIZ_TYPE_LABELS.CONTRACT || '合同'}
                      {contracts.length > 1 ? ` ${index + 1}` : ''}
                    </Typography.Text>
                    <Typography.Text type="secondary" ellipsis style={{ maxWidth: '100%' }}>
                      {item.url}
                    </Typography.Text>
                    <Space wrap>
                      {imageLike ? (
                        <Typography.Text type="secondary">点击缩略图放大查看</Typography.Text>
                      ) : null}
                      {pdf ? (
                        <Button type="link" size="small" style={{ padding: 0 }} onClick={() => setPdfUrl(item.url)}>
                          页内预览 PDF
                        </Button>
                      ) : null}
                      <Button
                        type="link"
                        size="small"
                        icon={<LinkOutlined />}
                        style={{ padding: 0 }}
                        href={item.url}
                        target="_blank"
                        rel="noreferrer"
                      >
                        新窗口打开
                      </Button>
                    </Space>
                  </Space>
                </div>
              );
            })}
          </Space>
        </div>
      )}

      <Modal
        title="合同预览"
        open={!!pdfUrl}
        onCancel={() => setPdfUrl(null)}
        width={900}
        footer={[
          <Button key="open" href={pdfUrl || undefined} target="_blank" rel="noreferrer">
            新窗口打开
          </Button>,
          <Button key="close" type="primary" onClick={() => setPdfUrl(null)}>
            关闭
          </Button>,
        ]}
        destroyOnClose
      >
        {pdfUrl ? (
          <iframe
            title="合同 PDF 预览"
            src={pdfUrl}
            style={{ width: '100%', height: '70vh', border: 'none', borderRadius: 8 }}
          />
        ) : null}
      </Modal>
    </div>
  );
}

type StoreRow = {
  id: string;
  name: string;
  phone: string;
  status: string;
  auditStatus?: string;
  rejectReason?: string | null;
  cityName: string;
  district: string;
  address: string;
  intro: string | null;
  coverUrl: string | null;
  createdAt: string;
  cityRef?: { name: string; code: string };
  partner?: { companyName: string };
  account?: {
    phone: string;
    name: string;
    status: string;
    bankAccountName?: string | null;
    bankAccountNo?: string | null;
    bankBranch?: string | null;
  };
  openTime?: string | null;
  closeTime?: string | null;
  openTime2?: string | null;
  closeTime2?: string | null;
  avgPrice?: number | null;
  settlementRate?: number;
  category?: { id: string; name: string; parentId?: string | null } | null;
};

type PartnerOption = { id: string; companyName: string };
type CityOption = {
  id: string;
  name: string;
  code: string;
  partnerBindings?: Array<{ partnerAccountId: string; partnerCompanyName?: string }>;
};
type CategoryNode = {
  id: string;
  name: string;
  status?: string;
  children?: CategoryNode[];
};

export default function StoresPage() {
  const navigate = useNavigate();
  const [form] = Form.useForm();
  const [editForm] = Form.useForm();
  const [createForm] = Form.useForm<StoreCreateForm>();
  const [filters, setFilters] = useState<Record<string, string>>({});
  const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<StoreRow>(
    '/admin/stores',
    () => {
      const qs = new URLSearchParams();
      if (filters.name) qs.set('name', filters.name);
      if (filters.status) qs.set('status', filters.status);
      if (filters.auditStatus) qs.set('auditStatus', filters.auditStatus);
      if (filters.phone) qs.set('phone', filters.phone);
      return qs;
    },
    [filters],
  );
  const [detail, setDetail] = useState<Record<string, unknown> | null>(null);
  const [drawerOpen, setDrawerOpen] = useState(false);
  const [rejectOpen, setRejectOpen] = useState(false);
  const [rejectReason, setRejectReason] = useState('');
  const [auditing, setAuditing] = useState(false);
  const [createOpen, setCreateOpen] = useState(false);
  const [createStep, setCreateStep] = useState(0);
  const [createError, setCreateError] = useState('');
  const [locating, setLocating] = useState(false);
  const [mapPickerOpen, setMapPickerOpen] = useState(false);
  const [mapPickerTarget, setMapPickerTarget] = useState<'create' | 'edit'>('create');
  const [partners, setPartners] = useState<PartnerOption[]>([]);
  const [cities, setCities] = useState<CityOption[]>([]);
  const [categoryTree, setCategoryTree] = useState<CategoryNode[]>([]);
  const [optionsLoading, setOptionsLoading] = useState(false);

  const selectedPartnerId = Form.useWatch('partnerAccountId', createForm);
  const selectedRegionCodes = Form.useWatch('regionCodes', createForm);
  const selectedCityId = Form.useWatch('cityId', createForm);
  const selectedCategoryParentId = Form.useWatch('categoryParentId', createForm);

  const categoryParentOptions = useMemo(
    () =>
      categoryTree
        .filter((n) => n.status !== 'INACTIVE')
        .map((n) => ({ value: n.id, label: n.name })),
    [categoryTree],
  );
  const categoryChildOptions = useMemo(() => {
    const parent = categoryTree.find((n) => n.id === selectedCategoryParentId);
    return (parent?.children ?? [])
      .filter((n) => n.status !== 'INACTIVE')
      .map((n) => ({ value: n.id, label: n.name }));
  }, [categoryTree, selectedCategoryParentId]);

  function bindRegionSelection(codes: string[], partnerAccountId?: string) {
    const binding = resolveRegionBinding(codes, cities, partnerAccountId ?? selectedPartnerId);
    if (!binding) {
      createForm.setFieldsValue({ regionCodes: codes, cityId: undefined });
      return;
    }
    createForm.setFieldsValue({
      regionCodes: codes,
      province: binding.region.province,
      city: binding.region.city,
      district: binding.region.district,
      districtCode: binding.region.districtCode,
      cityId: binding.cityId,
    });
  }

  const regionBindingHint = useMemo(() => {
    if (!selectedRegionCodes?.length) return null;
    const binding = resolveRegionBinding(selectedRegionCodes, cities, selectedPartnerId);
    if (!binding) return null;
    if (binding.cityId && binding.matchedCity) {
      return `已匹配开城城市：${binding.matchedCity.name}（区划 ${binding.cityCode}，区县 ${binding.region.districtCode}）`;
    }
    return `区划 ${binding.cityCode} 暂未开城，请先在「开城 → 城市」添加`;
  }, [selectedRegionCodes, cities, selectedPartnerId]);

  async function loadOptions() {
    setOptionsLoading(true);
    try {
      const qs = `pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`;
      const [p, c, cats] = await Promise.all([
        request<Paginated<PartnerOption>>(`/admin/partners?${qs}`),
        request<Paginated<CityOption>>(`/admin/cities?${qs}`),
        request<CategoryNode[]>('/admin/store-categories'),
      ]);
      setPartners(p.items);
      setCities(c.items);
      setCategoryTree(Array.isArray(cats) ? cats : []);
      if (!p.items.length) message.warning('暂无开城合伙人，请先在「开城 → 城市」详情中创建合伙人');
      if (!c.items.length) message.warning('暂无开城城市，请先在「开城 → 城市」中创建');
      if (!Array.isArray(cats) || !cats.length) {
        message.warning('暂无门店分类，请先在「门店 → 门店分类」中配置');
      }
    } catch (e) {
      message.error(e instanceof Error ? e.message : '加载合伙人/城市/分类失败');
    } finally {
      setOptionsLoading(false);
    }
  }

  function closeCreateModal() {
    setCreateOpen(false);
    setCreateStep(0);
    setCreateError('');
    createForm.resetFields();
  }

  function openCreateModal() {
    void loadOptions();
    createForm.setFieldsValue({
      envPhotoUrls: ['', '', ''],
      settlementRate: 60,
      openTime: '10:00',
      closeTime: '22:00',
      openTime2: undefined,
      closeTime2: undefined,
      avgPrice: undefined,
    });
    setCreateStep(0);
    setCreateError('');
    setCreateOpen(true);
  }

  async function handleCreateNext() {
    const values = createForm.getFieldsValue();
    if (createStep === 0) {
      const msg = validateStoreCreateStep1(values);
      if (msg) {
        setCreateError(msg);
        return;
      }
      try {
        await createForm.validateFields([
          'partnerAccountId',
          'regionCodes',
          'cityId',
          'categoryParentId',
          'categoryId',
          'name',
          'phone',
          'address',
        ]);
      } catch {
        return;
      }
    }
    setCreateError('');
    setCreateStep((s) => s + 1);
  }

  async function handleCreateSubmit() {
    try {
      const values = await createForm.validateFields();
      const step3Msg = validateStoreCreateStep3(values);
      if (step3Msg) {
        setCreateError(step3Msg);
        return;
      }

      const envPhotoUrls = (values.envPhotoUrls ?? []).map((u: string) => u?.trim()).filter(Boolean) as string[];
      await request('/admin/stores', {
        method: 'POST',
        body: JSON.stringify({
          partnerAccountId: values.partnerAccountId,
          cityId: values.cityId,
          categoryId: values.categoryId,
          province: values.province,
          city: values.city,
          name: values.name.trim(),
          phone: values.phone.trim(),
          district: values.district.trim(),
          address: values.address.trim(),
          ...(values.latitude != null &&
          values.longitude != null &&
          Number.isFinite(Number(values.latitude)) &&
          Number.isFinite(Number(values.longitude))
            ? {
                latitude: Number(values.latitude),
                longitude: Number(values.longitude),
              }
            : {}),
          intro: values.intro?.trim() || undefined,
          benefitUsageRule: values.benefitUsageRule?.trim() || undefined,
          openTime: values.openTime?.trim() || '10:00',
          closeTime: values.closeTime?.trim() || '22:00',
          ...(values.openTime2?.trim() && values.closeTime2?.trim()
            ? { openTime2: values.openTime2.trim(), closeTime2: values.closeTime2.trim() }
            : {}),
          ...(values.avgPrice != null && values.avgPrice !== ''
            ? { avgPrice: Number(values.avgPrice) }
            : {}),
          coverUrl: values.coverUrl?.trim() || undefined,
          envPhotoUrls: envPhotoUrls.length ? envPhotoUrls : undefined,
          contractUrl: values.contractUrl?.trim() || undefined,
          bankAccountName: values.bankAccountName.trim(),
          bankAccountNo: values.bankAccountNo.replace(/\s/g, ''),
          bankBranch: values.bankBranch.trim(),
          settlementRate: Number(values.settlementRate ?? 60) / 100,
        }),
      });
      message.success('门店已创建');
      closeCreateModal();
      void reload();
    } catch (e) {
      if (e && typeof e === 'object' && 'errorFields' in e) {
        const fields = e as { errorFields?: Array<{ name: string[] }> };
        const first = fields.errorFields?.[0]?.name?.[0];
        if (first === 'partnerAccountId' || first === 'cityId' || first === 'regionCodes' || first === 'name' || first === 'phone') {
          setCreateStep(0);
        }
        return;
      }
      message.error(e instanceof Error ? e.message : '创建失败');
    }
  }

  const columns: ColumnsType<StoreRow> = [
    {
      title: '封面', dataIndex: 'coverUrl', width: 72,
      render: (url) => url ? <Image src={url} width={48} height={48} style={{ objectFit: 'cover', borderRadius: 4 }} /> : '—',
    },
    { title: '门店名', dataIndex: 'name', width: 140 },
    {
      title: '分类',
      width: 100,
      render: (_, row) => row.category?.name || '—',
    },
    { title: '城市', dataIndex: 'cityName', width: 80 },
    { title: '电话', dataIndex: 'phone', width: 120 },
    {
      title: '营业状态', dataIndex: 'status', width: 90,
      render: (s) => <Tag>{STORE_STATUS_LABELS[s] || s}</Tag>,
    },
    {
      title: '审核', dataIndex: 'auditStatus', width: 100,
      render: (s, row) => {
        const status = s || 'APPROVED';
        const color = status === 'PENDING' ? 'orange' : status === 'REJECTED' ? 'red' : 'green';
        return (
          <Space direction="vertical" size={0}>
            <Tag color={color}>{STORE_AUDIT_STATUS_LABELS[status] || status}</Tag>
            {status === 'REJECTED' && row.rejectReason ? (
              <Typography.Text type="secondary" style={{ fontSize: 12 }} ellipsis>
                {row.rejectReason}
              </Typography.Text>
            ) : null}
          </Space>
        );
      },
    },
    { title: '开城合伙人', dataIndex: ['partner', 'companyName'], width: 120 },
    { title: '介绍', dataIndex: 'intro', width: 160, ellipsis: true, render: (v) => v || '—' },
    { title: '店长', dataIndex: ['account', 'name'], width: 90, render: (v) => v || '—' },
    { title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime },
    {
      title: '操作', width: 80,
      render: (_, row) => (
        <Button type="link" size="small" onClick={async () => {
          const d = await request<Record<string, unknown>>(`/admin/stores/${row.id}`);
          setDetail(d);
          editForm.setFieldsValue({
            name: d.name,
            phone: d.phone,
            intro: d.intro,
            benefitUsageRule: d.benefitUsageRule,
            coverUrl: d.coverUrl,
            address: d.address,
            district: d.district,
            latitude: d.latitude != null ? Number(d.latitude) : undefined,
            longitude: d.longitude != null ? Number(d.longitude) : undefined,
            settlementRate: d.settlementRate != null ? Number(d.settlementRate) * 100 : 60,
            openTime: d.openTime || '10:00',
            closeTime: d.closeTime || '22:00',
            openTime2: d.openTime2 || undefined,
            closeTime2: d.closeTime2 || undefined,
            avgPrice: d.avgPrice != null ? Number(d.avgPrice) : undefined,
          });
          setDrawerOpen(true);
        }}>详情</Button>
      ),
    },
  ];

  return (
    <div>
      <Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
        <Space direction="vertical" size={0}>
          <Typography.Title level={4} style={{ margin: 0 }}>门店</Typography.Title>
          <Typography.Text type="secondary">共 {data?.total ?? 0} 家门店（含合伙人录入）</Typography.Text>
        </Space>
        <Button type="primary" onClick={openCreateModal}>新建门店</Button>
      </Space>
      <Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
        <Form.Item name="name" label="名称"><Input allowClear /></Form.Item>
        <Form.Item name="phone" label="电话"><Input allowClear /></Form.Item>
        <Form.Item name="status" label="营业状态">
          <Select allowClear style={{ width: 100 }} placeholder="全部" options={Object.entries(STORE_STATUS_LABELS).map(([value, label]) => ({ value, label }))} />
        </Form.Item>
        <Form.Item name="auditStatus" label="审核">
          <Select
            allowClear
            style={{ width: 110 }}
            placeholder="全部"
            options={Object.entries(STORE_AUDIT_STATUS_LABELS).map(([value, label]) => ({ value, label }))}
          />
        </Form.Item>
        <Form.Item><Button type="primary" htmlType="submit">查询</Button></Form.Item>
        <Form.Item><Button onClick={() => { form.resetFields(); setFilters({}); setPage(1); }}>重置</Button></Form.Item>
      </Form>
      <Table rowKey="id" className="admin-table-nowrap" loading={loading} columns={columns} dataSource={data?.items ?? []} scroll={{ x: 1200 }}
        pagination={{ current: page, pageSize, total: data?.total ?? 0, showSizeChanger: true, onChange: (p, ps) => { setPage(p); setPageSize(ps); } }} />
      <Drawer title="门店详情" width={760} open={drawerOpen} onClose={() => setDrawerOpen(false)}
        extra={detail && (
          <Space wrap>
            {String(detail.auditStatus || '') === 'PENDING' || String(detail.auditStatus || '') === 'REJECTED' ? (
              <>
                <Button
                  type="primary"
                  loading={auditing}
                  onClick={async () => {
                    setAuditing(true);
                    try {
                      const updated = await request<Record<string, unknown>>(`/admin/stores/${detail.id}/audit`, {
                        method: 'PUT',
                        body: JSON.stringify({ approved: true, remark: '审核通过' }),
                      });
                      message.success('已通过审核，合伙人可开门营业');
                      setDetail({ ...detail, ...updated, auditStatus: 'APPROVED', rejectReason: null });
                      void reload();
                    } finally {
                      setAuditing(false);
                    }
                  }}
                >
                  通过
                </Button>
                <Button
                  danger
                  loading={auditing}
                  onClick={() => {
                    setRejectReason('');
                    setRejectOpen(true);
                  }}
                >
                  驳回
                </Button>
              </>
            ) : null}
            <Select defaultValue={String(detail.status)} style={{ width: 120 }}
              options={Object.entries(STORE_STATUS_LABELS).map(([value, label]) => ({ value, label }))}
              onChange={async (status) => {
                await request(`/admin/stores/${detail.id}/status`, { method: 'PUT', body: JSON.stringify({ status }) });
                message.success('状态已更新');
                setDetail({ ...detail, status });
                void reload();
              }} />
            <Button type="primary" onClick={async () => {
              const v = await editForm.validateFields();
              const hasCoords =
                v.latitude != null &&
                v.longitude != null &&
                Number.isFinite(Number(v.latitude)) &&
                Number.isFinite(Number(v.longitude));
              const payload = {
                name: v.name,
                phone: v.phone,
                coverUrl: v.coverUrl,
                intro: v.intro,
                benefitUsageRule: v.benefitUsageRule ?? null,
                district: v.district,
                address: v.address,
                avgPrice: v.avgPrice,
                openTime: v.openTime,
                closeTime: v.closeTime,
                openTime2: v.openTime2 || null,
                closeTime2: v.closeTime2 || null,
                settlementRate: v.settlementRate != null ? Number(v.settlementRate) / 100 : undefined,
                ...(hasCoords
                  ? { latitude: Number(v.latitude), longitude: Number(v.longitude) }
                  : {}),
              };
              await request(`/admin/stores/${detail.id}`, { method: 'PUT', body: JSON.stringify(payload) });
              message.success('已保存');
              setDetail({
                ...detail,
                ...payload,
                settlementRate: payload.settlementRate,
                latitude: hasCoords ? Number(v.latitude) : detail.latitude,
                longitude: hasCoords ? Number(v.longitude) : detail.longitude,
              });
              void reload();
            }}>保存</Button>
          </Space>
        )}>
        {detail && (
          <>
            <StoreAuditMediaSection detail={detail} />
            <Descriptions column={1} bordered size="small" style={{ marginBottom: 16 }}>
              <Descriptions.Item label="ID">{String(detail.id)}</Descriptions.Item>
              <Descriptions.Item label="门店分类">
                {detail.category && typeof detail.category === 'object' && 'name' in detail.category
                  ? String((detail.category as { name?: string }).name || '—')
                  : '—'}
              </Descriptions.Item>
              <Descriptions.Item label="审核状态">
                <Tag color={
                  String(detail.auditStatus) === 'PENDING' ? 'orange'
                    : String(detail.auditStatus) === 'REJECTED' ? 'red' : 'green'
                }>
                  {STORE_AUDIT_STATUS_LABELS[String(detail.auditStatus || 'APPROVED')] || String(detail.auditStatus)}
                </Tag>
              </Descriptions.Item>
              {String(detail.auditStatus) === 'REJECTED' ? (
                <Descriptions.Item label="驳回原因">{String(detail.rejectReason || '—')}</Descriptions.Item>
              ) : null}
              <Descriptions.Item label="地址">{String(detail.province)}{String(detail.cityName)}{String(detail.district)}{String(detail.address)}</Descriptions.Item>
              <Descriptions.Item label="经纬度">
                {detail.latitude != null && detail.longitude != null
                  ? `${Number(detail.latitude).toFixed(6)}, ${Number(detail.longitude).toFixed(6)}`
                  : '未设置'}
              </Descriptions.Item>
              <Descriptions.Item label="营业时间">
                {[
                  detail.openTime && detail.closeTime
                    ? `${String(detail.openTime)}-${String(detail.closeTime)}`
                    : null,
                  detail.openTime2 && detail.closeTime2
                    ? `${String(detail.openTime2)}-${String(detail.closeTime2)}`
                    : null,
                ]
                  .filter(Boolean)
                  .join('，') || '—'}
              </Descriptions.Item>
              <Descriptions.Item label="人均费用">
                {detail.avgPrice != null ? `¥${Number(detail.avgPrice).toFixed(0)}` : '—'}
              </Descriptions.Item>
              <Descriptions.Item label="核销结算比例">
                {detail.settlementRate != null ? `${Math.round(Number(detail.settlementRate) * 100)}%` : '60%'}
              </Descriptions.Item>
              <Descriptions.Item label="结算户名">
                {detail.account && typeof detail.account === 'object' && 'bankAccountName' in detail.account
                  ? String((detail.account as { bankAccountName?: string | null }).bankAccountName || '—')
                  : '—'}
              </Descriptions.Item>
              <Descriptions.Item label="开户银行">
                {detail.account && typeof detail.account === 'object' && 'bankBranch' in detail.account
                  ? String((detail.account as { bankBranch?: string | null }).bankBranch || '—')
                  : '—'}
              </Descriptions.Item>
              <Descriptions.Item label="银行卡号">
                {detail.account && typeof detail.account === 'object' && 'bankAccountNo' in detail.account
                  ? String((detail.account as { bankAccountNo?: string | null }).bankAccountNo || '—')
                  : '—'}
              </Descriptions.Item>
              <Descriptions.Item label="核销数">{String(detail.redeemCount ?? 0)}</Descriptions.Item>
              <Descriptions.Item label="操作">
                <Button type="link" size="small" style={{ padding: 0 }} onClick={() => navigate(`/logs/stores?storeId=${detail.id}`)}>
                  查看商户日志
                </Button>
              </Descriptions.Item>
              {Array.isArray(detail.audits) && (detail.audits as Array<Record<string, unknown>>).length > 0 ? (
                <Descriptions.Item label="审核记录">
                  <Space direction="vertical" size={4} style={{ width: '100%' }}>
                    {(detail.audits as Array<Record<string, unknown>>).map((a) => (
                      <Typography.Text key={String(a.id)} style={{ fontSize: 12 }}>
                        {fmtTime(String(a.createdAt))} · {String(a.status)} · {String(a.remark || '—')}
                      </Typography.Text>
                    ))}
                  </Space>
                </Descriptions.Item>
              ) : null}
            </Descriptions>
            <Form form={editForm} layout="vertical">
              <Form.Item name="name" label="名称" rules={[{ required: true }]}><Input /></Form.Item>
              <Form.Item name="phone" label="电话" rules={[{ required: true }]}><Input /></Form.Item>
              <Form.Item name="coverUrl" label="封面图">
                <OssUpload bizType="STORE_TITLE" mediaType="IMAGE" />
              </Form.Item>
              <Form.Item name="intro" label="介绍"><Input.TextArea rows={4} showCount maxLength={500} /></Form.Item>
              <Form.Item
                name="benefitUsageRule"
                label="好客权益券使用规则"
                extra="展示在用户端门店详情「门店详情」下方"
              >
                <Input.TextArea rows={4} placeholder="选填，最多1000字" showCount maxLength={1000} />
              </Form.Item>
              <Form.Item name="district" label="区县"><Input /></Form.Item>
              <Form.Item name="address" label="详细地址"><Input /></Form.Item>
              <Space wrap style={{ width: '100%' }}>
                <Form.Item name="latitude" label="纬度" style={{ marginBottom: 8 }}>
                  <InputNumber
                    style={{ width: 180 }}
                    precision={7}
                    step={0.000001}
                    placeholder="如 34.7466000"
                  />
                </Form.Item>
                <Form.Item name="longitude" label="经度" style={{ marginBottom: 8 }}>
                  <InputNumber
                    style={{ width: 180 }}
                    precision={7}
                    step={0.000001}
                    placeholder="如 113.6253000"
                  />
                </Form.Item>
              </Space>
              <Space wrap style={{ marginBottom: 16 }}>
                <Button
                  loading={locating}
                  onClick={() => {
                    fillGeolocation(
                      (lat, lng) => editForm.setFieldsValue({ latitude: lat, longitude: lng }),
                      setLocating,
                    );
                  }}
                >
                  获取当前位置
                </Button>
                <Button
                  icon={<EnvironmentOutlined />}
                  onClick={() => {
                    setMapPickerTarget('edit');
                    setMapPickerOpen(true);
                  }}
                >
                  腾讯地图选点
                </Button>
                <Typography.Text type="secondary">
                  搜索或拖图确认位置后自动填入经纬度
                </Typography.Text>
              </Space>
              <Form.Item name="avgPrice" label="人均费用（选填）">
                <InputNumber min={0} precision={0} style={{ width: '100%' }} addonAfter="元" placeholder="用户端展示" />
              </Form.Item>
              <Space wrap style={{ width: '100%' }}>
                <Form.Item name="openTime" label="营业开始" rules={[{ required: true }]}>
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
                <Form.Item name="closeTime" label="营业结束" rules={[{ required: true }]}>
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
              </Space>
              <Space wrap style={{ width: '100%' }}>
                <Form.Item name="openTime2" label="第二段开始（选填）">
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
                <Form.Item name="closeTime2" label="第二段结束">
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
              </Space>
              <Form.Item name="settlementRate" label="核销结算比例 %" initialValue={60} rules={[{ required: true }]}>
                <InputNumber min={0} max={100} precision={2} style={{ width: '100%' }} addonAfter="%" />
              </Form.Item>
            </Form>
          </>
        )}
      </Drawer>
      <Modal
        title="新建门店"
        open={createOpen}
        width={640}
        onCancel={closeCreateModal}
        destroyOnClose
        footer={(
          <Space>
            {createStep > 0 && <Button onClick={() => { setCreateError(''); setCreateStep((s) => s - 1); }}>上一步</Button>}
            {createStep < 2 ? (
              <Button type="primary" onClick={() => void handleCreateNext()}>下一步</Button>
            ) : (
              <Button type="primary" onClick={() => void handleCreateSubmit()}>提交</Button>
            )}
          </Space>
        )}
      >
        <Steps current={createStep} items={CREATE_STEPS} style={{ marginBottom: 24 }} />
        {createError && (
          <Alert type="error" message={createError} showIcon style={{ marginBottom: 16 }} />
        )}
        <Form form={createForm} layout="vertical" preserve>
          <div style={{ display: createStep === 0 ? 'block' : 'none' }}>
              <Form.Item name="partnerAccountId" label="开城合伙人" rules={[{ required: true, message: '请选择开城合伙人' }]}>
                <Select
                  showSearch
                  loading={optionsLoading}
                  optionFilterProp="label"
                  placeholder={optionsLoading ? '加载中…' : '请选择开城合伙人'}
                  options={partners.map((p) => ({ value: p.id, label: p.companyName }))}
                  onChange={(partnerAccountId) => {
                    const codes = createForm.getFieldValue('regionCodes') as string[] | undefined;
                    if (codes?.length === 3) bindRegionSelection(codes, partnerAccountId);
                    else createForm.setFieldValue('cityId', undefined);
                  }}
                />
              </Form.Item>
              <Form.Item
                name="regionCodes"
                label="所在地区"
                rules={[{ required: true, message: '请选择省 / 市 / 区县' }]}
                extra={regionBindingHint ? (
                  <Typography.Text type={selectedCityId ? 'secondary' : 'warning'}>
                    {regionBindingHint}
                  </Typography.Text>
                ) : null}
              >
                <ChinaRegionCascader onChange={(codes) => bindRegionSelection(codes)} />
              </Form.Item>
              <Form.Item name="cityId" hidden rules={[{ required: true, message: '请选择所在地区以匹配开城城市' }]}>
                <Input />
              </Form.Item>
              <Form.Item name="province" hidden><Input /></Form.Item>
              <Form.Item name="city" hidden><Input /></Form.Item>
              <Form.Item name="district" hidden><Input /></Form.Item>
              <Form.Item name="districtCode" hidden><Input /></Form.Item>
              <Form.Item
                name="categoryParentId"
                label="门店分类（大类）"
                rules={[{ required: true, message: '请选择门店大类' }]}
              >
                <Select
                  showSearch
                  loading={optionsLoading}
                  optionFilterProp="label"
                  placeholder={optionsLoading ? '加载中…' : '选择大类'}
                  options={categoryParentOptions}
                  onChange={() => createForm.setFieldValue('categoryId', undefined)}
                />
              </Form.Item>
              <Form.Item
                name="categoryId"
                label="门店分类（细类）"
                rules={[{ required: true, message: '请选择门店细类' }]}
                extra={
                  <Typography.Link onClick={() => navigate('/store-categories')}>
                    去配置门店分类
                  </Typography.Link>
                }
              >
                <Select
                  showSearch
                  optionFilterProp="label"
                  placeholder={selectedCategoryParentId ? '选择细类' : '请先选大类'}
                  disabled={!selectedCategoryParentId}
                  options={categoryChildOptions}
                />
              </Form.Item>
              <Form.Item name="name" label="门店名称" rules={[{ required: true, message: '请填写门店名称' }]}>
                <Input placeholder="请输入门店名称" />
              </Form.Item>
              <Form.Item name="phone" label="门店手机号（登录账号）" rules={[{ required: true, message: '请填写门店手机号' }]}>
                <Input placeholder="11位手机号" />
              </Form.Item>
              <Form.Item name="address" label="详细地址" rules={[{ required: true, message: '请填写详细地址' }]}>
                <Input.TextArea rows={2} placeholder="请输入详细门牌号" />
              </Form.Item>
              <Space wrap style={{ width: '100%' }}>
                <Form.Item name="latitude" label="纬度" style={{ marginBottom: 8 }}>
                  <InputNumber
                    style={{ width: 180 }}
                    precision={7}
                    step={0.000001}
                    placeholder="如 34.7466000"
                  />
                </Form.Item>
                <Form.Item name="longitude" label="经度" style={{ marginBottom: 8 }}>
                  <InputNumber
                    style={{ width: 180 }}
                    precision={7}
                    step={0.000001}
                    placeholder="如 113.6253000"
                  />
                </Form.Item>
              </Space>
              <Space wrap style={{ marginBottom: 16 }}>
                <Button
                  loading={locating}
                  onClick={() => {
                    fillGeolocation(
                      (lat, lng) => createForm.setFieldsValue({ latitude: lat, longitude: lng }),
                      setLocating,
                    );
                  }}
                >
                  获取当前位置
                </Button>
                <Button
                  icon={<EnvironmentOutlined />}
                  onClick={() => {
                    setMapPickerTarget('create');
                    setMapPickerOpen(true);
                  }}
                >
                  腾讯地图选点
                </Button>
                <Typography.Text type="secondary">
                  可手动填写 / 定位 / 腾讯地图选点填入坐标
                </Typography.Text>
              </Space>
              <Space wrap style={{ width: '100%' }}>
                <Form.Item name="openTime" label="营业开始" rules={[{ required: true, message: '请填写营业开始时间' }]}>
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
                <Form.Item name="closeTime" label="营业结束" rules={[{ required: true, message: '请填写营业结束时间' }]}>
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
              </Space>
              <Space wrap style={{ width: '100%' }}>
                <Form.Item name="openTime2" label="第二段开始（选填）">
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
                <Form.Item name="closeTime2" label="第二段结束">
                  <Input type="time" style={{ width: 140 }} />
                </Form.Item>
              </Space>
              <Form.Item name="avgPrice" label="人均费用（选填）">
                <InputNumber min={0} precision={0} style={{ width: '100%' }} addonAfter="元" placeholder="用户端展示" />
              </Form.Item>
              <Form.Item name="intro" label="门店简介">
                <Input.TextArea rows={3} placeholder="选填，2~500字" showCount maxLength={500} />
              </Form.Item>
              <Form.Item
                name="benefitUsageRule"
                label="好客权益券使用规则"
                extra="展示在用户端门店详情"
              >
                <Input.TextArea rows={3} placeholder="选填，最多1000字" showCount maxLength={1000} />
              </Form.Item>
          </div>
          <div style={{ display: createStep === 1 ? 'block' : 'none' }}>
              <Typography.Paragraph type="secondary">
                preV1 照片上传为选填，可直接下一步（与合伙人端一致）。
              </Typography.Paragraph>
              <Form.Item name="coverUrl" label="门头照">
                <OssUpload bizType="STORE_TITLE" mediaType="IMAGE" />
              </Form.Item>
              <Typography.Text strong>环境照片</Typography.Text>
              <Form.List name="envPhotoUrls">
                {(fields) => (
                  <div style={{ marginTop: 8 }}>
                    {fields.map((field, index) => (
                      <Form.Item key={field.key} name={field.name} label={`环境图 ${index + 1}`}>
                        <OssUpload bizType="STORE_ENV" mediaType="IMAGE" />
                      </Form.Item>
                    ))}
                  </div>
                )}
              </Form.List>
              <Form.Item name="contractUrl" label="签约合同">
                <OssUpload bizType="STORE_CONTRACT" mediaType="FILE" accept="image/*,.pdf" />
              </Form.Item>
          </div>
          <div style={{ display: createStep === 2 ? 'block' : 'none' }}>
              <Form.Item name="bankAccountName" label="户主姓名" rules={[{ required: true, message: '请填写户主姓名' }]}>
                <Input placeholder="银行卡实名姓名" />
              </Form.Item>
              <Form.Item name="bankAccountNo" label="银行卡号" rules={[{ required: true, message: '请填写银行卡号' }]}>
                <Input placeholder="16~19位银行卡号" />
              </Form.Item>
              <Form.Item name="bankBranch" label="开户支行" rules={[{ required: true, message: '请填写开户支行' }]}>
                <Input placeholder="例如：中国工商银行洛阳分行" />
              </Form.Item>
              <Form.Item name="settlementRate" label="核销结算比例 %" initialValue={60} rules={[{ required: true, message: '请填写结算比例' }]}>
                <InputNumber min={0} max={100} precision={2} style={{ width: '100%' }} addonAfter="%" />
              </Form.Item>
              <Alert
                type="info"
                showIcon
                message="请确保银行卡信息准确，以免影响门店餐费结算。"
              />
          </div>
        </Form>
      </Modal>

      <Modal
        title="驳回门店审核"
        open={rejectOpen}
        okText="确认驳回"
        okButtonProps={{ danger: true, loading: auditing, disabled: !rejectReason.trim() }}
        onCancel={() => setRejectOpen(false)}
        onOk={async () => {
          if (!detail || !rejectReason.trim()) return;
          setAuditing(true);
          try {
            const updated = await request<Record<string, unknown>>(`/admin/stores/${detail.id}/audit`, {
              method: 'PUT',
              body: JSON.stringify({ approved: false, remark: rejectReason.trim() }),
            });
            message.success('已驳回，原因已同步合伙人端');
            setDetail({ ...detail, ...updated, auditStatus: 'REJECTED', rejectReason: rejectReason.trim() });
            setRejectOpen(false);
            void reload();
          } finally {
            setAuditing(false);
          }
        }}
      >
        <Typography.Paragraph type="secondary">驳回原因将展示给合伙人，请说明需修改的内容。</Typography.Paragraph>
        <Input.TextArea
          rows={4}
          maxLength={200}
          showCount
          placeholder="请填写驳回原因"
          value={rejectReason}
          onChange={(e) => setRejectReason(e.target.value)}
        />
      </Modal>
      <TencentLocPickerModal
        open={mapPickerOpen}
        onClose={() => setMapPickerOpen(false)}
        latitude={
          mapPickerTarget === 'edit'
            ? Number(editForm.getFieldValue('latitude') ?? detail?.latitude)
            : Number(createForm.getFieldValue('latitude'))
        }
        longitude={
          mapPickerTarget === 'edit'
            ? Number(editForm.getFieldValue('longitude') ?? detail?.longitude)
            : Number(createForm.getFieldValue('longitude'))
        }
        onPick={(loc) => {
          if (mapPickerTarget === 'edit') {
            editForm.setFieldsValue({
              latitude: loc.latitude,
              longitude: loc.longitude,
              ...(loc.address && !editForm.getFieldValue('address')
                ? { address: loc.address }
                : {}),
            });
          } else {
            createForm.setFieldsValue({
              latitude: loc.latitude,
              longitude: loc.longitude,
              ...(loc.address && !createForm.getFieldValue('address')
                ? { address: loc.address }
                : {}),
            });
          }
          message.success(
            `已选点 ${loc.latitude.toFixed(6)}, ${loc.longitude.toFixed(6)}${loc.name ? `（${loc.name}）` : ''}`,
          );
        }}
      />
    </div>
  );
}
