import { useEffect, useMemo, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import {
  Alert,
  Button,
  Checkbox,
  Descriptions,
  Drawer,
  Form,
  Image,
  Input,
  InputNumber,
  Modal,
  Select,
  Space,
  Steps,
  Switch,
  Table,
  Tabs,
  Tag,
  Typography,
  message,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { FormInstance } from 'antd/es/form';
import { EnvironmentOutlined } from '@ant-design/icons';
import { isStoreContactPhone, STORE_CONTACT_PHONE_HINT } from '@dukang/domain';
import { request, type Paginated } from '../lib/api';
import {
  ADMIN_OPTIONS_PAGE_SIZE,
  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 MultiImageUpload from '../components/MultiImageUpload';
import TencentLocPickerModal from '../components/TencentLocPickerModal';
import AdminStorePackagesSection, {
  type AdminStorePackagesHandle,
} from '../components/AdminStorePackagesSection';

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 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 StoreAuditMediaEditor() {
  return (
    <div style={{ marginBottom: 16 }}>
      <Alert
        type="info"
        showIcon
        style={{ marginBottom: 16 }}
        message="可替换或删除门头照 / 环境照 / 签约合同，点击右上角「保存修改」后生效。环境照、签约合同均最多 20 张。套餐请在「套餐」页签编辑，同样由「保存修改」一并提交。"
      />
      <Form.Item name="coverUrl" label="门头照">
        <OssUpload bizType="STORE_TITLE" mediaType="IMAGE" />
      </Form.Item>
      <Form.Item
        name="envPhotoUrls"
        label="环境照片"
        extra="建议至少 3 张；支持批量上传，最多 20 张。可逐张删除后保存。"
      >
        <MultiImageUpload
          bizType="STORE_ENV"
          mediaType="IMAGE"
          maxCount={20}
          tip="环境照支持一次选择多张批量上传"
        />
      </Form.Item>
      <Form.Item
        name="contractUrls"
        label="签约合同"
        style={{ marginTop: 16 }}
        extra="支持多张合同照片（如首页、盖章页），也可上传 PDF，最多 20 个。"
      >
        <MultiImageUpload
          bizType="STORE_CONTRACT"
          mediaType="FILE"
          accept="image/*,.pdf"
          maxCount={20}
          buttonText="批量上传合同"
          tip="合同支持一次选择多张照片批量上传，最多 20 个"
        />
      </Form.Item>
    </div>
  );
}

function StoreVisibilityWhitelistFields({ form }: { form: FormInstance }) {
  const enabled = Form.useWatch('visibilityWhitelistEnabled', form);

  return (
    <>
      <Form.Item
        name="visibilityWhitelistEnabled"
        label="可见白名单"
        valuePropName="checked"
        extra="开启后仅全局测试白名单内手机号在 C 端可见，用于在线测试"
      >
        <Switch checkedChildren="开" unCheckedChildren="关" />
      </Form.Item>
      {enabled ? (
        <Typography.Text type="secondary" style={{ display: 'block', marginBottom: 16 }}>
          可见手机号见白名单管理
        </Typography.Text>
      ) : null}
    </>
  );
}

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;
  visibilityWhitelistEnabled?: boolean;
  visibilityPhones?: string[];
  isTest?: boolean;
  cityRef?: { name: string; code: string };
  partner?: { id?: string; companyName?: string | null; name?: string | null; phone?: string | null };
  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;
  sortOrder?: number;
  category?: { id: string; name: string; parentId?: string | null } | null;
};

type PartnerOption = { id: string; companyName?: string | null; name?: string | null; phone?: string | null };

function partnerOptionLabel(p: PartnerOption): string {
  const company = (p.companyName || '').trim();
  const person = (p.name || '').trim();
  const phone = (p.phone || '').trim();
  if (company && person) return `${company}-${person}`;
  return company || person || phone || p.id;
}
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 [searchParams] = useSearchParams();
  const initialCityId = searchParams.get('cityId') ?? '';
  const initialPartnerId = searchParams.get('partnerId') ?? '';
  const [form] = Form.useForm();
  const [editForm] = Form.useForm();
  const [createForm] = Form.useForm<StoreCreateForm>();
  const [filters, setFilters] = useState<Record<string, string | boolean>>(() => {
    const init: Record<string, string | boolean> = {};
    if (initialCityId) init.cityId = initialCityId;
    if (initialPartnerId) init.partnerId = initialPartnerId;
    return init;
  });
  const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<StoreRow>(
    '/admin/stores',
    () => {
      const qs = new URLSearchParams();
      if (filters.name) qs.set('name', String(filters.name));
      if (filters.status) qs.set('status', String(filters.status));
      if (filters.auditStatus) qs.set('auditStatus', String(filters.auditStatus));
      if (filters.phone) qs.set('phone', String(filters.phone));
      if (filters.cityId) qs.set('cityId', String(filters.cityId));
      if (filters.partnerId) qs.set('partnerId', String(filters.partnerId));
      if (filters.excludeTest) qs.set('excludeTest', 'true');
      return qs;
    },
    [filters],
  );
  const [filterCities, setFilterCities] = useState<CityOption[]>([]);
  const [filterPartners, setFilterPartners] = useState<PartnerOption[]>([]);
  const [detail, setDetail] = useState<Record<string, unknown> | null>(null);
  const [drawerOpen, setDrawerOpen] = useState(false);
  const packagesRef = useRef<AdminStorePackagesHandle>(null);
  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 [saving, setSaving] = useState(false);
  const [phoneMismatch, setPhoneMismatch] = useState<string | null>(null);

  useEffect(() => {
    void request<Paginated<CityOption>>(`/admin/cities?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
      .then((res) => setFilterCities(res.items))
      .catch(() => {});
    void request<Paginated<PartnerOption>>(`/admin/partners?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
      .then((res) => setFilterPartners(res.items))
      .catch(() => {});
  }, []);

  useEffect(() => {
    const cityId = searchParams.get('cityId') ?? '';
    const partnerId = searchParams.get('partnerId') ?? '';
    let changed = false;
    setFilters((prev) => {
      const next = { ...prev };
      if ((prev.cityId ?? '') !== cityId) {
        changed = true;
        if (cityId) next.cityId = cityId;
        else delete next.cityId;
      }
      if ((prev.partnerId ?? '') !== partnerId) {
        changed = true;
        if (partnerId) next.partnerId = partnerId;
        else delete next.partnerId;
      }
      return changed ? next : prev;
    });
    form.setFieldsValue({
      cityId: cityId || undefined,
      partnerId: partnerId || undefined,
    });
    if (cityId || partnerId) setPage(1);
  }, [searchParams, form, setPage]);

  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 editCategoryParentId = Form.useWatch('categoryParentId', editForm);

  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]);

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

  async function openStoreDetail(row: StoreRow) {
    const [d, cats] = await Promise.all([
      request<Record<string, unknown>>(`/admin/stores/${row.id}`),
      request<CategoryNode[]>('/admin/store-categories').catch(() => [] as CategoryNode[]),
    ]);
    setCategoryTree(Array.isArray(cats) ? cats : []);
    setDetail(d);
    const category = d.category && typeof d.category === 'object'
      ? (d.category as { id?: string; parentId?: string | null })
      : null;
    const account = d.account && typeof d.account === 'object'
      ? (d.account as {
          phone?: string | null;
          bankAccountName?: string | null;
          bankAccountNo?: string | null;
          bankBranch?: string | null;
        })
      : null;
    const categoryId = category?.id != null ? String(category.id) : undefined;
    let parentId = category?.parentId != null ? String(category.parentId) : undefined;
    if (!parentId && categoryId) {
      for (const parent of cats) {
        if (parent.id === categoryId) {
          parentId = parent.id;
          break;
        }
        if ((parent.children ?? []).some((c) => c.id === categoryId)) {
          parentId = parent.id;
          break;
        }
      }
    }
    const loginPhone =
      (typeof d.loginPhone === 'string' && d.loginPhone) ||
      account?.phone ||
      (typeof d.phone === 'string' ? d.phone : undefined);
    const storePhone = typeof d.phone === 'string' ? d.phone : undefined;
    const contactPhone =
      (typeof d.contactPhone === 'string' && d.contactPhone.trim()) ||
      storePhone ||
      loginPhone ||
      '';
    const phoneMismatchNow = !!(loginPhone && storePhone && loginPhone !== storePhone);
    setPhoneMismatch(phoneMismatchNow ? String(loginPhone) : null);
    editForm.setFieldsValue({
      name: d.name,
      // 登录手机号（老板）；与 StoreAccount 同步
      phone: loginPhone || storePhone,
      contactPhone,
      intro: d.intro,
      benefitUsageRule:
        d.benefitUsageRule != null &&
        String(d.benefitUsageRule).trim() &&
        !/^null$/i.test(String(d.benefitUsageRule).trim())
          ? String(d.benefitUsageRule)
          : '',
      coverUrl: d.coverUrl,
      envPhotoUrls: (() => {
        const { envs } = collectMediaUrls(d);
        const urls = envs.map((item) => item.url).filter(Boolean);
        return urls;
      })(),
      contractUrls: (() => {
        const { contracts } = collectMediaUrls(d);
        return contracts.map((item) => item.url).filter(Boolean);
      })(),
      province: d.province,
      city: d.cityName,
      district: d.district,
      address: d.address,
      categoryParentId: parentId,
      categoryId,
      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,
      bankAccountName: account?.bankAccountName || undefined,
      bankAccountNo: account?.bankAccountNo || undefined,
      bankBranch: account?.bankBranch || undefined,
      visibilityWhitelistEnabled: !!d.visibilityWhitelistEnabled,
      visibilityPhones: Array.isArray(d.visibilityPhones)
        ? (d.visibilityPhones as string[])
        : [],
      isTest: !!d.isTest,
      sortOrder: d.sortOrder != null ? Number(d.sortOrder) : 0,
    });
    setDrawerOpen(true);
  }

  async function saveStoreDetail() {
    if (!detail) return;
    setSaving(true);
    try {
      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,
        contactPhone: String(v.contactPhone || '').trim() || v.phone,
        coverUrl: v.coverUrl ?? '',
        envPhotoUrls: Array.isArray(v.envPhotoUrls)
          ? v.envPhotoUrls.map((u: string) => String(u || '').trim()).filter(Boolean)
          : [],
        contractUrls: Array.isArray(v.contractUrls)
          ? v.contractUrls.map((u: string) => String(u || '').trim()).filter(Boolean)
          : [],
        intro: v.intro,
        benefitUsageRule:
          typeof v.benefitUsageRule === 'string' &&
          v.benefitUsageRule.trim() &&
          !/^null$/i.test(v.benefitUsageRule.trim())
            ? v.benefitUsageRule.trim()
            : null,
        categoryId: v.categoryId,
        province: v.province,
        city: v.city,
        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,
        bankAccountName: v.bankAccountName ?? null,
        bankAccountNo: v.bankAccountNo ?? null,
        bankBranch: v.bankBranch ?? null,
        visibilityWhitelistEnabled: !!v.visibilityWhitelistEnabled,
        isTest: !!v.isTest,
        sortOrder: v.sortOrder != null ? Number(v.sortOrder) : 0,
        ...(hasCoords
          ? { latitude: Number(v.latitude), longitude: Number(v.longitude) }
          : {}),
      };
      const updated = await request<Record<string, unknown>>(`/admin/stores/${detail.id}`, {
        method: 'PUT',
        body: JSON.stringify(payload),
      });
      const packagesResult = await packagesRef.current?.saveIfLoaded({ quiet: true });
      message.success(
        packagesResult?.skipped === false ? '门店信息与套餐已保存' : '门店信息已保存',
      );
      setDetail(updated);
      setPhoneMismatch(null);
      void reload();
    } catch (e) {
      if (e && typeof e === 'object' && 'errorFields' in e) return;
      message.error(e instanceof Error ? e.message : '保存失败');
    } finally {
      setSaving(false);
    }
  }

  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(opts?: { quiet?: boolean }) {
    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 (!opts?.quiet) {
        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,
      sortOrder: 0,
      openTime: '10:00',
      closeTime: '22:00',
      openTime2: undefined,
      closeTime2: undefined,
      avgPrice: undefined,
      visibilityWhitelistEnabled: false,
      visibilityPhones: [],
    });
    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[];
      const contractUrls = (values.contractUrls ?? []).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(),
          contactPhone: String(values.contactPhone || 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,
          contractUrls: contractUrls.length ? contractUrls : undefined,
          bankAccountName: values.bankAccountName.trim(),
          bankAccountNo: values.bankAccountNo.replace(/\s/g, ''),
          bankBranch: values.bankBranch.trim(),
          settlementRate: Number(values.settlementRate ?? 60) / 100,
          sortOrder: values.sortOrder != null ? Number(values.sortOrder) : 0,
          visibilityWhitelistEnabled: !!values.visibilityWhitelistEnabled,
        }),
      });
      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: 180,
      ellipsis: { showTitle: false },
      render: (v: string, row) => {
        const name = v || '—';
        return (
          <Space size={4} style={{ maxWidth: '100%' }} wrap={false}>
            <Typography.Text ellipsis={{ tooltip: name }} style={{ maxWidth: row.isTest ? 110 : 160 }}>
              {name}
            </Typography.Text>
            {row.isTest ? <Tag color="orange">测试</Tag> : null}
          </Space>
        );
      },
    },
    {
      title: '分类',
      width: 100,
      ellipsis: true,
      render: (_, row) => row.category?.name || '—',
    },
    { title: '城市', dataIndex: 'cityName', width: 80, ellipsis: true },
    { title: '登录号', dataIndex: 'phone', width: 120 },
    {
      title: '联系电话',
      dataIndex: 'contactPhone',
      width: 120,
      render: (v: string | null | undefined, row) => v || row.phone,
    },
    {
      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} style={{ maxWidth: '100%' }}>
            <Tag color={color}>{STORE_AUDIT_STATUS_LABELS[status] || status}</Tag>
            {status === 'REJECTED' && row.rejectReason ? (
              <Typography.Text type="secondary" style={{ fontSize: 12, maxWidth: 88 }} ellipsis={{ tooltip: row.rejectReason }}>
                {row.rejectReason}
              </Typography.Text>
            ) : null}
          </Space>
        );
      },
    },
    {
      title: '开城合伙人',
      dataIndex: 'partner',
      width: 140,
      ellipsis: { showTitle: false },
      render: (partner: StoreRow['partner']) => {
        if (!partner) return '—';
        const label = partnerOptionLabel({ id: partner.id ?? '', ...partner });
        return (
          <Typography.Text ellipsis={{ tooltip: label }} style={{ maxWidth: 124 }}>
            {label}
          </Typography.Text>
        );
      },
    },
    {
      title: '可见',
      dataIndex: 'visibilityWhitelistEnabled',
      width: 90,
      render: (v) =>
        v ? <Tag color="orange">限测</Tag> : <Tag>公开</Tag>,
    },
    { title: '介绍', dataIndex: 'intro', width: 160, ellipsis: true, render: (v) => v || '—' },
    { title: '排序', dataIndex: 'sortOrder', width: 70 },
    { title: '店长', dataIndex: ['account', 'name'], width: 90, ellipsis: true, render: (v) => v || '—' },
    { title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime },
    {
      title: '操作',
      width: 140,
      fixed: 'right',
      render: (_, row) => (
        <Space size={0}>
          <Button type="link" size="small" onClick={() => void openStoreDetail(row)}>详情</Button>
          <Button type="link" size="small" onClick={() => navigate(`/store-ratings?storeId=${row.id}`)}>评价</Button>
        </Space>
      ),
    },
  ];

  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="cityId" label="城市">
          <Select
            allowClear
            showSearch
            optionFilterProp="label"
            style={{ width: 140 }}
            placeholder="全部"
            options={filterCities.map((c) => ({ value: c.id, label: c.name }))}
          />
        </Form.Item>
        <Form.Item name="partnerId" label="城市合伙人">
          <Select
            allowClear
            showSearch
            optionFilterProp="label"
            style={{ width: 180 }}
            placeholder="全部"
            options={filterPartners.map((p) => ({ value: p.id, label: partnerOptionLabel(p) }))}
          />
        </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 name="excludeTest" valuePropName="checked">
          <Checkbox>过滤测试账号</Checkbox>
        </Form.Item>
        <Form.Item><Button type="primary" htmlType="submit">查询</Button></Form.Item>
        <Form.Item>
          <Button
            onClick={() => {
              form.resetFields();
              setFilters({});
              setPage(1);
              if (searchParams.has('cityId') || searchParams.has('partnerId')) {
                navigate('/stores', { replace: true });
              }
            }}
          >
            重置
          </Button>
        </Form.Item>
      </Form>
      <Table
        rowKey="id"
        className="admin-table-nowrap"
        loading={loading}
        columns={columns}
        dataSource={data?.items ?? []}
        scroll={{ x: 1720 }}
        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 value={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" loading={saving} onClick={() => void saveStoreDetail()}>保存修改</Button>
          </Space>
        )}>
        {detail && (
          <Form form={editForm} layout="vertical">
            <Tabs
              destroyInactiveTabPane={false}
              items={[
                {
                  key: 'basic',
                  label: '基本信息',
                  forceRender: true,
                  children: (
                    <>
                      <Descriptions column={1} bordered size="small" style={{ marginBottom: 16 }}>
                        <Descriptions.Item label="ID">{String(detail.id)}</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.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>
                      <Typography.Title level={5} style={{ marginTop: 0 }}>编辑门店信息</Typography.Title>
                      {phoneMismatch ? (
                        <Alert
                          type="warning"
                          showIcon
                          style={{ marginBottom: 16 }}
                          message={`主账号登录号为 ${phoneMismatch}，与门店登录字段不一致。保存「登录手机号」将同步到门店端登录账号。`}
                        />
                      ) : null}
                      <Form.Item name="name" label="名称" rules={[{ required: true }]}><Input /></Form.Item>
                      <Form.Item
                        name="phone"
                        label="登录手机号（老板）"
                        rules={[{ required: true }]}
                        extra="门店端主账号短信登录；修改后需用新号重新登录"
                      >
                        <Input />
                      </Form.Item>
                      <Form.Item
                        name="contactPhone"
                        label="联系电话（店长/对外）"
                        rules={[
                          { required: true, message: '请填写对外联系电话' },
                          {
                            validator: (_, value) =>
                              isStoreContactPhone(String(value || ''))
                                ? Promise.resolve()
                                : Promise.reject(new Error(STORE_CONTACT_PHONE_HINT)),
                          },
                        ]}
                        extra="用户端门店详情展示与拨号使用此号码，可与登录号不同，支持座机"
                      >
                        <Input placeholder="手机号或座机，如 0379-8888888" />
                      </Form.Item>
                      <Form.Item
                        name="categoryParentId"
                        label="门店分类（大类）"
                        rules={[{ required: true, message: '请选择门店大类' }]}
                      >
                        <Select
                          showSearch
                          loading={optionsLoading}
                          optionFilterProp="label"
                          options={categoryParentOptions}
                          onChange={() => editForm.setFieldValue('categoryId', undefined)}
                        />
                      </Form.Item>
                      <Form.Item
                        name="categoryId"
                        label="门店分类（细类）"
                        rules={[{ required: true, message: '请选择门店细类' }]}
                      >
                        <Select
                          showSearch
                          optionFilterProp="label"
                          placeholder={editCategoryParentId ? '选择细类' : '请先选大类'}
                          disabled={!editCategoryParentId}
                          options={editCategoryChildOptions}
                        />
                      </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>
                      <Space wrap style={{ width: '100%' }}>
                        <Form.Item name="province" label="省份" rules={[{ required: true }]}>
                          <Input style={{ width: 140 }} />
                        </Form.Item>
                        <Form.Item name="city" label="城市" rules={[{ required: true }]}>
                          <Input style={{ width: 140 }} />
                        </Form.Item>
                        <Form.Item name="district" label="区县" rules={[{ required: true }]}>
                          <Input style={{ width: 140 }} />
                        </Form.Item>
                      </Space>
                      <Form.Item name="address" label="详细地址" rules={[{ required: true }]}>
                        <Input.TextArea rows={2} />
                      </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>
                      </Space>
                      <Form.Item name="avgPrice" label="人均费用（选填）">
                        <InputNumber min={0} precision={0} style={{ width: '100%' }} addonAfter="元" placeholder="用户端展示" />
                      </Form.Item>
                      <Form.Item
                        name="sortOrder"
                        label="排序"
                        extra="数值越小越靠前；同排序时按距离（有定位）或创建时间"
                      >
                        <InputNumber min={0} precision={0} style={{ width: '100%' }} placeholder="0" />
                      </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>
                      <StoreVisibilityWhitelistFields form={editForm} />
                      <Form.Item
                        name="isTest"
                        label="测试门店"
                        valuePropName="checked"
                        extra="测试门店核销不计入结算账单；联系电话命中全局白名单时会自动标记"
                      >
                        <Switch checkedChildren="是" unCheckedChildren="否" />
                      </Form.Item>
                    </>
                  ),
                },
                {
                  key: 'settlement',
                  label: '结算资质',
                  forceRender: true,
                  children: (
                    <>
                      <Form.Item name="settlementRate" label="核销结算比例 %" initialValue={60} rules={[{ required: true }]}>
                        <InputNumber min={0} max={100} precision={2} style={{ width: '100%' }} addonAfter="%" />
                      </Form.Item>
                      <Form.Item name="bankAccountName" label="结算户名">
                        <Input placeholder="开户名" />
                      </Form.Item>
                      <Form.Item name="bankBranch" label="开户银行">
                        <Input placeholder="如 中国工商银行某某支行" />
                      </Form.Item>
                      <Form.Item
                        name="bankAccountNo"
                        label="银行卡号"
                        rules={[
                          {
                            validator: async (_, value) => {
                              const v = String(value || '').trim();
                              if (!v) return;
                              if (!/^\d{16,19}$/.test(v)) {
                                throw new Error('银行卡号须为 16–19 位数字');
                              }
                            },
                          },
                        ]}
                      >
                        <Input placeholder="16–19 位数字" maxLength={19} />
                      </Form.Item>
                      <Typography.Paragraph type="secondary">
                        修改后点右上角「保存修改」一并提交。
                      </Typography.Paragraph>
                    </>
                  ),
                },
                {
                  key: 'media',
                  label: '审核材料',
                  forceRender: true,
                  children: <StoreAuditMediaEditor />,
                },
                {
                  key: 'packages',
                  label: '套餐',
                  forceRender: true,
                  children: <AdminStorePackagesSection ref={packagesRef} storeId={String(detail.id)} />,
                },
              ]}
            />
          </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: partnerOptionLabel(p) }))}
                  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="门店端主账号登录" />
              </Form.Item>
              <Form.Item
                name="contactPhone"
                label="联系电话（店长/对外）"
                extra="用户端拨号展示；留空则与登录号相同。支持座机"
                rules={[
                  {
                    validator: (_, value) => {
                      const raw = String(value || '').trim();
                      if (!raw || isStoreContactPhone(raw)) return Promise.resolve();
                      return Promise.reject(new Error(STORE_CONTACT_PHONE_HINT));
                    },
                  },
                ]}
              >
                <Input placeholder="手机号或座机，如 0379-8888888" />
              </Form.Item>
              <Form.Item name="sortOrder" label="排序" extra="数值越小越靠前">
                <InputNumber min={0} precision={0} style={{ width: '100%' }} placeholder="0" />
              </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>
              <StoreVisibilityWhitelistFields form={createForm} />
          </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>
              <Form.Item
                name="envPhotoUrls"
                label="环境照片"
                extra="选填；支持批量上传，最多 20 张"
              >
                <MultiImageUpload
                  bizType="STORE_ENV"
                  mediaType="IMAGE"
                  maxCount={20}
                  tip="环境照支持一次选择多张批量上传"
                />
              </Form.Item>
              <Form.Item
                name="contractUrls"
                label="签约合同"
                extra="选填；支持多张合同照片或 PDF，最多 20 个"
              >
                <MultiImageUpload
                  bizType="STORE_CONTRACT"
                  mediaType="FILE"
                  accept="image/*,.pdf"
                  maxCount={20}
                  buttonText="批量上传合同"
                  tip="合同支持一次选择多张照片批量上传，最多 20 个"
                />
              </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>
  );
}
