const { useState: useStateRecommend, useEffect: useEffectRecommend, useMemo: useMemoRecommend, useRef: useRefRecommend } = React;

const RECOMMEND_FLOOR_PLAN_OPTIONS = ['1R', '1K', '1DK', '1LDK', '2K', '2DK', '2LDK', '3K', '3DK', '3LDK', '4K', '4DK', '4LDK'];
const RECOMMEND_DEFAULT_VISIBLE_COUNT = 5;
const RECOMMEND_RENT_MIN = 40000;
const RECOMMEND_RENT_MAX = 500000;
const RECOMMEND_RENT_STEP = 5000;
const RECOMMEND_CONTACT_METHODS = [
  { key: 'wechat', label: '微信号' },
  { key: 'phone', label: '手机号码' },
];
const RECOMMEND_LEAFLET_JS = 'https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js';
const RECOMMEND_LEAFLET_CSS = 'https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css';

let recommendLeafletPromise = null;

function createRecommendFilters(regionKey = '') {
  return {
    contactMethod: 'wechat',
    contactValue: '',
    regionKey: regionKey || '',
    areaKey: '',
    districtName: '',
    operatorName: '',
    lineId: '',
    station: '',
    keyword: '',
    minRent: RECOMMEND_RENT_MIN,
    maxRent: RECOMMEND_RENT_MAX,
    floorPlans: [],
    vacancyOnly: false,
  };
}

function normalizeRecommendText(value) {
  return String(value || '').trim().toLowerCase();
}

function loadRecommendLeaflet() {
  if (window.L) return Promise.resolve(window.L);
  if (recommendLeafletPromise) return recommendLeafletPromise;

  recommendLeafletPromise = new Promise((resolve, reject) => {
    if (!document.querySelector(`link[href="${RECOMMEND_LEAFLET_CSS}"]`)) {
      const link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = RECOMMEND_LEAFLET_CSS;
      document.head.appendChild(link);
    }

    const existing = document.querySelector(`script[src="${RECOMMEND_LEAFLET_JS}"]`);
    if (existing) {
      existing.addEventListener('load', () => resolve(window.L));
      existing.addEventListener('error', reject);
      return;
    }

    const script = document.createElement('script');
    script.src = RECOMMEND_LEAFLET_JS;
    script.async = true;
    script.onload = () => resolve(window.L);
    script.onerror = reject;
    document.head.appendChild(script);
  });

  return recommendLeafletPromise;
}

function extractRecommendFloorPlanTokens(value) {
  return (String(value || '').toUpperCase().match(/\d+(?:R|K|DK|LDK)/g) || []);
}

function recommendFloorPlanScore(value) {
  return RECOMMEND_FLOOR_PLAN_OPTIONS.indexOf(String(value || '').toUpperCase());
}

function recommendPropertyMatchesFloorPlans(floorPlanText, selectedPlans) {
  if (!selectedPlans?.length) return true;
  const normalized = String(floorPlanText || '').toUpperCase().replace(/\s+/g, '');
  if (!normalized) return false;

  if (selectedPlans.some((plan) => normalized.includes(String(plan).toUpperCase()))) {
    return true;
  }

  const rangeText = normalized.split('/')[0] || normalized;
  const tokens = extractRecommendFloorPlanTokens(rangeText);
  if (tokens.length >= 2 && rangeText.includes('～')) {
    const start = recommendFloorPlanScore(tokens[0]);
    const end = recommendFloorPlanScore(tokens[tokens.length - 1]);
    if (start >= 0 && end >= 0) {
      const min = Math.min(start, end);
      const max = Math.max(start, end);
      return selectedPlans.some((plan) => {
        const score = recommendFloorPlanScore(plan);
        return score >= min && score <= max;
      });
    }
  }

  return false;
}

function recommendPropertyMatchesStation(property, stationName) {
  const normalizedStation = normalizeRecommendText(stationName);
  if (!normalizedStation) return true;
  return (property.stations || []).some((item) => normalizeRecommendText(item) === normalizedStation)
    || normalizeRecommendText(property.accessText).includes(normalizedStation)
    || normalizeRecommendText(property.detailTransportText).includes(normalizedStation);
}

function recommendPropertyMatchesLine(property, line) {
  if (!line) return true;
  const normalizedLineName = normalizeRecommendText(line.lineName);
  if (!normalizedLineName) return true;

  if (
    normalizeRecommendText(property.accessText).includes(normalizedLineName)
    || normalizeRecommendText(property.detailTransportText).includes(normalizedLineName)
  ) {
    return true;
  }

  const propertyStationSet = new Set((property.stations || []).map((item) => normalizeRecommendText(item)).filter(Boolean));
  return (line.stations || []).some((station) => propertyStationSet.has(normalizeRecommendText(station.stationName)));
}

function recommendPropertyMatchesOperator(property, operator) {
  if (!operator?.lines?.length) return true;
  return operator.lines.some((line) => recommendPropertyMatchesLine(property, line));
}

function parseRecommendRentRange(value) {
  return parseRentRange(String(value || '').replace(/[（(][^）)]*[）)]/g, '').trim());
}

function formatRecommendManYen(value) {
  const amount = Number(value || 0);
  if (!Number.isFinite(amount) || amount <= 0) return '0万';
  const man = amount / 10000;
  if (man < 10) return `${man.toFixed(1)}万`;
  return Number.isInteger(man) ? `${man}万` : `${man.toFixed(1)}万`;
}

function filterRecommendProperties(properties, filters, detailedOperators, stationDirectory) {
  if (!filters?.regionKey) return [];

  const currentOperator = detailedOperators.find((item) => item.operatorName === filters.operatorName) || null;
  const currentLine = (stationDirectory?.lines || []).find((item) => item.lineId === filters.lineId) || null;
  const keyword = normalizeRecommendText(filters.keyword);

  return properties
    .filter((item) => {
      if (filters.areaKey && item.areaKey !== filters.areaKey) return false;
      if (filters.districtName && normalizeDistrictName(item.districtName || item.skcs) !== filters.districtName) return false;
      if (filters.operatorName && currentOperator && !recommendPropertyMatchesOperator(item, currentOperator)) return false;
      if (filters.lineId && currentLine && !recommendPropertyMatchesLine(item, currentLine)) return false;
      if (filters.station && !recommendPropertyMatchesStation(item, filters.station)) return false;

      const rentRange = parseRecommendRentRange(item.detailRentText || item.rent || item.rent);
      if (rentRange.max < Number(filters.minRent)) return false;
      if (rentRange.min > Number(filters.maxRent)) return false;
      if (filters.vacancyOnly && !item.hasVacancy) return false;
      if (!recommendPropertyMatchesFloorPlans(item.floorPlanText, filters.floorPlans)) return false;

      if (keyword) {
        const haystack = [
          item.name,
          item.districtName,
          item.skcs,
          item.areaName,
          item.address,
          item.propertyKey,
          item.accessText,
          item.detailTransportText,
          ...(item.stations || []),
        ];
        if (!haystack.some((value) => normalizeRecommendText(value).includes(keyword))) {
          return false;
        }
      }

      return true;
    })
    .sort((left, right) => parseRecommendRentRange(left.detailRentText || left.rent).min - parseRecommendRentRange(right.detailRentText || right.rent).min);
}

function RecommendScreen({ initialRegionKey = '', onOpenListingRoom, onOpenWatchProperty, stateSnapshot, onStateChange }) {
  const initialState = stateSnapshot || {
    prompt: '',
    answer: '',
    filters: null,
    properties: [],
    resultCount: 0,
    visibleCount: RECOMMEND_DEFAULT_VISIBLE_COUNT,
    resultView: 'list',
    mapLocations: [],
    scrollTop: 0,
    anchorPropertyKey: '',
    anchorRestorePending: false,
  };
  const [prompt, setPrompt] = useStateRecommend(() => initialState.prompt || '');
  const [answer, setAnswer] = useStateRecommend(() => initialState.answer || '');
  const [filters, setFilters] = useStateRecommend(() => initialState.filters || null);
  const [properties, setProperties] = useStateRecommend(() => initialState.properties || []);
  const [resultCount, setResultCount] = useStateRecommend(() => Number(initialState.resultCount || 0) || 0);
  const [visibleCount, setVisibleCount] = useStateRecommend(() => Number(initialState.visibleCount) || RECOMMEND_DEFAULT_VISIBLE_COUNT);
  const [resultView, setResultView] = useStateRecommend(() => initialState.resultView === 'map' ? 'map' : 'list');
  const [mapLocations, setMapLocations] = useStateRecommend(() => Array.isArray(initialState.mapLocations) ? initialState.mapLocations : []);
  const [mapLoading, setMapLoading] = useStateRecommend(false);
  const [mapError, setMapError] = useStateRecommend('');
  const [loading, setLoading] = useStateRecommend(false);
  const [error, setError] = useStateRecommend('');
  const scrollRef = useRefRecommend(null);
  const restoredScrollRef = useRefRecommend(false);
  const latestStateRef = useRefRecommend(initialState);

  const visibleProperties = properties.slice(0, visibleCount);
  const canLoadMore = properties.length > visibleCount;

  useEffectRecommend(() => {
    latestStateRef.current = {
      prompt,
      answer,
      filters,
      properties,
      resultCount,
      visibleCount,
      resultView,
      mapLocations,
      scrollTop: scrollRef.current?.scrollTop || 0,
      anchorPropertyKey: latestStateRef.current?.anchorPropertyKey || '',
      anchorRestorePending: latestStateRef.current?.anchorRestorePending || false,
    };
    onStateChange && onStateChange(latestStateRef.current);
  }, [answer, filters, mapLocations, onStateChange, prompt, properties, resultCount, resultView, visibleCount]);

  useEffectRecommend(() => {
    if (restoredScrollRef.current || stateSnapshot?.anchorRestorePending) return;
    restoredScrollRef.current = true;
    const nextScrollTop = Number(stateSnapshot?.scrollTop || 0);
    if (!nextScrollTop) return;
    window.requestAnimationFrame(() => {
      window.requestAnimationFrame(() => {
        if (scrollRef.current) scrollRef.current.scrollTop = nextScrollTop;
      });
    });
  }, []);

  useEffectRecommend(() => (
    () => {
      if (!onStateChange) return;
      const current = latestStateRef.current || {};
      onStateChange({
        ...current,
        scrollTop: scrollRef.current?.scrollTop || 0,
      });
    }
  ), [onStateChange]);

  useEffectRecommend(() => {
    if (!stateSnapshot?.anchorRestorePending) return;
    const targetKey = stateSnapshot.anchorPropertyKey;
    if (!targetKey || !scrollRef.current) return;

    const targetElement = scrollRef.current.querySelector(`[data-recommend-card-key="${targetKey}"]`);
    if (!targetElement) return;

    const container = scrollRef.current;
    const rawTop = targetElement.offsetTop - Math.max(0, (container.clientHeight - targetElement.offsetHeight) / 2);
    const maxTop = Math.max(0, container.scrollHeight - container.clientHeight);
    const nextTop = Math.max(0, Math.min(rawTop, maxTop));

    window.requestAnimationFrame(() => {
      container.scrollTop = nextTop;
      const nextSnapshot = {
        ...latestStateRef.current,
        scrollTop: nextTop,
        anchorRestorePending: false,
      };
      latestStateRef.current = nextSnapshot;
      onStateChange && onStateChange(nextSnapshot);
    });
  }, [onStateChange, stateSnapshot?.anchorPropertyKey, stateSnapshot?.anchorRestorePending, visibleProperties.length]);

  function rememberRecommendState(anchorPropertyKey = '') {
    const snapshot = {
      prompt,
      answer,
      filters,
      properties,
      resultCount,
      visibleCount,
      resultView,
      mapLocations,
      scrollTop: scrollRef.current?.scrollTop || 0,
      anchorPropertyKey,
      anchorRestorePending: Boolean(anchorPropertyKey),
    };
    latestStateRef.current = snapshot;
    onStateChange && onStateChange(snapshot);
  }

  async function handleSubmit() {
    const nextPrompt = String(prompt || '').trim();
    if (!nextPrompt) {
      setError('请先输入你的找房需求。');
      return;
    }
    if (nextPrompt.length > 200) {
      setError('需求最多输入 200 个字。');
      return;
    }

    setError('');
    setAnswer('');
    setFilters(null);
    setProperties([]);
    setResultCount(0);
    setVisibleCount(RECOMMEND_DEFAULT_VISIBLE_COUNT);
    setResultView('list');
    setMapLocations([]);
    setMapError('');
    setLoading(true);
    try {
      const result = await subscribeApiFetch('/ai-recommend', {
        method: 'POST',
        body: { prompt: nextPrompt },
      });
      setAnswer(result.answer || '');
      setFilters(result.filters || null);
      setProperties(Array.isArray(result.properties) ? result.properties : []);
      setResultCount(Number(result.resultCount || 0) || 0);
      setVisibleCount(RECOMMEND_DEFAULT_VISIBLE_COUNT);
      setResultView('list');
      setMapLocations([]);
      setMapError('');
    } catch (issue) {
      setAnswer('');
      setFilters(null);
      setProperties([]);
      setResultCount(0);
      setResultView('list');
      setMapLocations([]);
      setMapError('');
      setError(issue.message === 'rate_limited' ? '请求有点频繁，请稍后再试。' : 'AI 荐房暂时没有成功，请稍后再试。');
    } finally {
      setLoading(false);
    }
  }

  function resetDraft() {
    setPrompt('');
    setAnswer('');
    setFilters(null);
    setProperties([]);
    setResultCount(0);
    setVisibleCount(RECOMMEND_DEFAULT_VISIBLE_COUNT);
    setResultView('list');
    setMapLocations([]);
    setMapError('');
    setError('');
  }

  async function openMapView() {
    setResultView('map');
    if (!properties.length || mapLocations.length > 0 || mapLoading) return;
    setMapError('');
    setMapLoading(true);
    try {
      const result = await subscribeApiFetch('/map-locations', {
        method: 'POST',
        body: {
          properties: properties.slice(0, 20).map((property) => ({
            propertyKey: property.propertyKey,
            name: property.name,
            regionName: property.regionName,
            districtName: property.districtName || property.skcs || property.areaName,
            skcs: property.skcs,
            areaName: property.areaName,
            address: property.address,
            rent: property.rent,
            detailRentText: property.detailRentText,
            hasVacancy: property.hasVacancy,
            activeRoomCount: property.activeRoomCount,
            roomCount: property.roomCount,
            rooms: property.rooms || [],
          })),
        },
      });
      setMapLocations(Array.isArray(result.locations) ? result.locations : []);
    } catch (issue) {
      setMapError(issue.message === 'rate_limited' ? '请求有点频繁，请稍后再试。' : '地图位置暂时加载失败，请稍后再试。');
    } finally {
      setMapLoading(false);
    }
  }

  function useExample(text) {
    setPrompt(text);
    setError('');
  }

  const promptLength = String(prompt || '').length;

  return (
    <div style={recommendStyles.shell}>
      <div
        ref={scrollRef}
        style={recommendStyles.scroll}
        onScroll={() => {
          latestStateRef.current = {
            prompt,
            answer,
            filters,
            properties,
            resultCount,
            visibleCount,
            resultView,
            mapLocations,
            scrollTop: scrollRef.current?.scrollTop || 0,
          };
        }}
      >
        <section style={recommendStyles.formCard}>
          <div style={recommendStyles.sectionHead}>
            <div>
              <div style={recommendStyles.sectionTitle}>AI 荐房</div>
            </div>
          </div>

          <label style={recommendStyles.aiField}>
            <textarea
              style={recommendStyles.aiTextarea}
              value={prompt}
              maxLength={200}
              onChange={(event) => {
                setPrompt(event.target.value);
                if (error) setError('');
              }}
              placeholder="例如：帮我找下福冈东区目前可租的2室以上的房子"
            />
            <div style={recommendStyles.aiMetaRow}>
              <span>地区、预算、房型、车站都可以直接写</span>
              <span>{promptLength}/200</span>
            </div>
          </label>

          <div style={recommendStyles.exampleRow}>
            {[
              '福冈东区目前可租的2室以上',
              '东京20万以内靠近地铁的1LDK',
              '大阪现在有空房的便宜2DK',
            ].map((item) => (
              <button key={item} type="button" style={recommendStyles.exampleChip} onClick={() => useExample(item)}>
                {item}
              </button>
            ))}
          </div>

          {error ? <div style={recommendStyles.error}>{error}</div> : null}

          <div style={recommendStyles.actionRow}>
            <button
              type="button"
              style={recommendStyles.primaryButton}
              onClick={handleSubmit}
              disabled={loading}
            >
              {loading ? '正在找房…' : '开始荐房'}
            </button>
            <button type="button" style={recommendStyles.secondaryButton} onClick={resetDraft}>
              清空
            </button>
          </div>
        </section>

        {answer || properties.length > 0 || filters ? (
          <section style={recommendStyles.resultsCard}>
            <div style={recommendStyles.sectionHead}>
              <div>
                <div style={recommendStyles.sectionTitle}>推荐结果</div>
              </div>
              {properties.length > 0 ? (
                <button
                  type="button"
                  style={recommendStyles.mapToggleButton}
                  onClick={() => {
                    if (resultView === 'map') {
                      setResultView('list');
                    } else {
                      openMapView();
                    }
                  }}
                >
                  {resultView === 'map' ? '列表查看' : '地图查看'}
                </button>
              ) : null}
            </div>

            {answer ? <div style={recommendStyles.aiAnswer}>{answer}</div> : null}

            {loading ? (
              <div style={recommendStyles.emptyState}>正在理解需求并筛选公开房源…</div>
            ) : properties.length === 0 ? (
              <div style={recommendStyles.emptyState}>当前没有匹配物件，可以换个地区、预算或放宽房型再试。</div>
            ) : resultView === 'map' ? (
              <RecommendMapView
                locations={mapLocations}
                loading={mapLoading}
                error={mapError}
                properties={properties}
                onOpenProperty={(selectedPropertyKey) => {
                  const selectedProperty = properties.find((item) => item.propertyKey === selectedPropertyKey);
                  rememberRecommendState(selectedPropertyKey);
                  onOpenWatchProperty && onOpenWatchProperty(selectedProperty?.propertyKey || selectedPropertyKey);
                }}
              />
            ) : (
              <>
                <div style={recommendStyles.resultList}>
                  {visibleProperties.map((property) => (
                    <RecommendPropertyCard
                      key={property.propertyKey}
                      property={property}
                      onOpenProperty={(selectedProperty) => {
                        rememberRecommendState(selectedProperty.propertyKey);
                        onOpenWatchProperty && onOpenWatchProperty(selectedProperty.propertyKey);
                      }}
                      onOpenRoom={(selectedProperty, room) => {
                        rememberRecommendState(selectedProperty.propertyKey);
                        onOpenListingRoom && onOpenListingRoom(selectedProperty, room);
                      }}
                    />
                  ))}
                </div>

                {canLoadMore ? (
                  <button type="button" style={recommendStyles.loadMoreButton} onClick={() => setVisibleCount((count) => count + RECOMMEND_DEFAULT_VISIBLE_COUNT)}>
                    更多
                  </button>
                ) : null}
              </>
            )}
          </section>
        ) : null}
      </div>
    </div>
  );
}

function RecommendRentSlider({ min, max, onChange }) {
  const minPercent = ((min - RECOMMEND_RENT_MIN) / (RECOMMEND_RENT_MAX - RECOMMEND_RENT_MIN)) * 100;
  const maxPercent = ((max - RECOMMEND_RENT_MIN) / (RECOMMEND_RENT_MAX - RECOMMEND_RENT_MIN)) * 100;

  function handleMinChange(value) {
    const nextMin = Math.min(Number(value), max - RECOMMEND_RENT_STEP);
    onChange({ min: nextMin, max });
  }

  function handleMaxChange(value) {
    const nextMax = Math.max(Number(value), min + RECOMMEND_RENT_STEP);
    onChange({ min, max: nextMax });
  }

  return (
    <div style={recommendStyles.sliderCard}>
      <div style={recommendStyles.sliderValueRow}>
        <div style={recommendStyles.sliderValueBox}>
          <div style={recommendStyles.sliderValueLabel}>最低</div>
          <div style={recommendStyles.sliderValueMain}>¥{formatRecommendManYen(min)}</div>
        </div>
        <div style={recommendStyles.sliderValueBox}>
          <div style={recommendStyles.sliderValueLabel}>最高</div>
          <div style={recommendStyles.sliderValueMain}>¥{formatRecommendManYen(max)}</div>
        </div>
      </div>

      <div style={recommendStyles.sliderTrackWrap}>
        <div style={recommendStyles.sliderTrackBase}></div>
        <div
          style={{
            ...recommendStyles.sliderTrackFill,
            left: `${minPercent}%`,
            width: `${Math.max(0, maxPercent - minPercent)}%`,
          }}
        ></div>
        <input
          type="range"
          min={RECOMMEND_RENT_MIN}
          max={RECOMMEND_RENT_MAX}
          step={RECOMMEND_RENT_STEP}
          value={min}
          onChange={(event) => handleMinChange(event.target.value)}
          style={recommendStyles.rangeInput}
        />
        <input
          type="range"
          min={RECOMMEND_RENT_MIN}
          max={RECOMMEND_RENT_MAX}
          step={RECOMMEND_RENT_STEP}
          value={max}
          onChange={(event) => handleMaxChange(event.target.value)}
          style={recommendStyles.rangeInput}
        />
      </div>

      <div style={recommendStyles.sliderTickRow}>
        <span>4.0万</span>
        <span>20万</span>
        <span>35万</span>
        <span>50万</span>
      </div>
    </div>
  );
}

function RecommendPropertyCard({ property, onOpenProperty, onOpenRoom }) {
  const activeRooms = (property.rooms || []).filter((room) => room.status !== 'expired');

  if (activeRooms.length > 0) {
    return (
      <div data-recommend-card-key={property.propertyKey}>
        <PropertyCard
          property={property}
          onRoomClick={(selectedProperty, room) => onOpenRoom && onOpenRoom(selectedProperty, room)}
        />
      </div>
    );
  }

  const thumbUrl = resolveImg(property.propertyImageLocalUrl || property.propertyImageRemoteUrl || property.image);
  const jpyCnyRate = window.UrExchange.useJpyCnyRate();
  const renderYenWithCny = (value) => window.UrExchange.renderYenWithCny(value, jpyCnyRate, {
    fontSize: '0.76em',
    marginLeft: 4,
    opacity: 0.82,
  });

  return (
    <article style={recommendStyles.propertyCard} data-recommend-card-key={property.propertyKey}>
      <div style={recommendStyles.propertyThumb}>
        {thumbUrl ? (
          <img src={thumbUrl} alt={property.name} style={recommendStyles.propertyThumbImg} />
        ) : (
          <BuildingPlaceholder seed={(property.name || '').length * 7} width={84} height={84} />
        )}
      </div>

      <div style={recommendStyles.propertyBody}>
        <div style={recommendStyles.propertyTopRow}>
          <div style={recommendStyles.propertyName}>{property.name}</div>
          <div style={{
            ...recommendStyles.vacancyBadge,
            background: '#F4EFE4',
            color: '#8A7F72',
          }}>
            暂无空房
          </div>
        </div>

        <div style={recommendStyles.propertyMeta}>
          {property.regionName || '—'} · {property.districtName || property.skcs || property.areaName || '—'}
        </div>
        <div style={recommendStyles.propertyAddress}>{property.address || property.accessText || property.detailTransportText || '—'}</div>
        <div style={recommendStyles.propertyRent}>{renderYenWithCny(property.detailRentText || property.rent || '—')}</div>
        <div style={recommendStyles.propertySubMeta}>房型 {property.floorPlanText || '—'}</div>

        <div style={recommendStyles.tagRow}>
          {(property.stations || []).slice(0, 3).map((station) => (
            <span key={station} style={recommendStyles.tag}>{station}站</span>
          ))}
          {property.areaName ? <span style={recommendStyles.tag}>{property.areaName}</span> : null}
        </div>

        <div style={recommendStyles.propertyActions}>
          <button
            type="button"
            style={recommendStyles.inlineButton}
            onClick={() => onOpenProperty && onOpenProperty(property)}
          >
            查看物业
          </button>
        </div>
      </div>
    </article>
  );
}

function escapeRecommendHtml(value) {
  return String(value || '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function RecommendMapView({ locations, loading, error, onOpenProperty }) {
  const mapRef = useRefRecommend(null);
  const mapInstanceRef = useRefRecommend(null);

  useEffectRecommend(() => {
    if (!mapRef.current || loading || error || !locations.length) return undefined;
    let disposed = false;

    loadRecommendLeaflet().then((L) => {
      if (disposed || !mapRef.current) return;
      if (mapInstanceRef.current) {
        mapInstanceRef.current.remove();
        mapInstanceRef.current = null;
      }

      const first = locations[0];
      const map = L.map(mapRef.current, {
        zoomControl: true,
        attributionControl: true,
      }).setView([first.lat, first.lng], 12);
      mapInstanceRef.current = map;

      L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: '<a href="https://maps.gsi.go.jp/development/ichiran.html" target="_blank" rel="noopener">地理院タイル</a>',
      }).addTo(map);

      const bounds = [];
      locations.forEach((location) => {
        const lat = Number(location.lat);
        const lng = Number(location.lng);
        if (!Number.isFinite(lat) || !Number.isFinite(lng)) return;
        bounds.push([lat, lng]);
        const popup = `
          <div style="min-width:180px">
            <div style="font-weight:800;margin-bottom:4px">${escapeRecommendHtml(location.name)}</div>
            <div style="font-size:12px;color:#6B6258;line-height:1.5">${escapeRecommendHtml(location.districtName || location.address || '')}</div>
            <div style="font-size:12px;color:#C6572B;font-weight:800;margin-top:6px">${escapeRecommendHtml(location.rent || '')}</div>
            <button type="button" data-map-property-key="${escapeRecommendHtml(location.propertyKey)}" style="width:100%;margin-top:8px;border:0;border-radius:10px;background:#1A1A1A;color:#fff;padding:8px 10px;font-weight:800">查看物件</button>
          </div>
        `;
        L.marker([lat, lng]).addTo(map).bindPopup(popup);
      });

      if (bounds.length > 1) {
        map.fitBounds(bounds, { padding: [28, 28], maxZoom: 14 });
      }
      window.requestAnimationFrame(() => map.invalidateSize());
    }).catch(() => {
      if (mapRef.current) {
        mapRef.current.innerHTML = '<div style="padding:18px;color:#8A7F72;font-size:13px">地图组件加载失败，请稍后再试。</div>';
      }
    });

    return () => {
      disposed = true;
      if (mapInstanceRef.current) {
        mapInstanceRef.current.remove();
        mapInstanceRef.current = null;
      }
    };
  }, [error, loading, locations]);

  useEffectRecommend(() => {
    const current = mapRef.current;
    if (!current) return undefined;
    const handleClick = (event) => {
      const target = event.target?.closest?.('[data-map-property-key]');
      if (!target) return;
      const propertyKey = target.getAttribute('data-map-property-key');
      if (propertyKey) onOpenProperty && onOpenProperty(propertyKey);
    };
    current.addEventListener('click', handleClick);
    return () => current.removeEventListener('click', handleClick);
  }, [onOpenProperty]);

  if (loading) {
    return <div style={recommendStyles.emptyState}>正在解析物件地址并加载地图…</div>;
  }
  if (error) {
    return <div style={recommendStyles.error}>{error}</div>;
  }
  if (!locations.length) {
    return <div style={recommendStyles.emptyState}>这些物件暂时没有可用坐标，先用列表查看更稳。</div>;
  }

  return (
    <div style={recommendStyles.mapWrap}>
      <div ref={mapRef} style={recommendStyles.mapCanvas}></div>
      <div style={recommendStyles.mapHint}>
        地图使用国土地理院免费底图，位置由地址解析生成，可能不是楼栋精确点。
      </div>
    </div>
  );
}

function RecommendPropertyDetail({ property, onBack, onOpenRoom }) {
  const [galleryIndex, setGalleryIndex] = useStateRecommend(0);
  const mergedProperty = property || {};
  const activeRooms = (mergedProperty.rooms || []).filter((room) => room.status !== 'expired');
  const jpyCnyRate = window.UrExchange.useJpyCnyRate();
  const renderYenWithCny = (value) => window.UrExchange.renderYenWithCny(value, jpyCnyRate, {
    fontSize: '0.76em',
    marginLeft: 4,
    opacity: 0.8,
  });

  const gallery = useMemoRecommend(() => {
    const images = [];
    const seen = new Set();
    const pushImage = (url, label) => {
      const resolved = resolveImg(url);
      if (!resolved || seen.has(resolved)) return;
      seen.add(resolved);
      images.push({ url: resolved, label });
    };

    (mergedProperty.galleryImages || []).forEach((item, index) => {
      pushImage(item.localUrl || item.url || item.remoteUrl, normalizeWatchGalleryLabel(item, index));
    });
    pushImage(mergedProperty.propertyImageLocalUrl || mergedProperty.propertyImageRemoteUrl || mergedProperty.image, '物件外观');
    return images;
  }, [mergedProperty]);

  return (
    <div style={recommendStyles.detailShell}>
      <div style={recommendStyles.detailTopBar}>
        <button style={recommendStyles.detailBackBtn} onClick={onBack}>
          <Icon.Chevron size={16} dir="left" color="#1A1A1A"/>
        </button>
        <div style={recommendStyles.detailTopTitle}>
          <div style={recommendStyles.detailTopMain}>物业详情</div>
          <div style={recommendStyles.detailTopSub}>{mergedProperty.name || mergedProperty.propertyKey}</div>
        </div>
      </div>

      <div style={recommendStyles.detailScroll}>
        <div style={recommendStyles.detailGalleryWrap}>
          <div style={recommendStyles.detailGalleryMain}>
            {gallery[galleryIndex] ? (
              <img src={gallery[galleryIndex].url} alt={gallery[galleryIndex].label} style={recommendStyles.detailGalleryImg}/>
            ) : (
              <div style={recommendStyles.detailGalleryFallback}>
                <BuildingPlaceholder seed={(mergedProperty.name || '').length * 9} width={260} height={200}/>
              </div>
            )}
          </div>
          {gallery.length > 1 ? (
            <div style={recommendStyles.detailThumbRow}>
              {gallery.map((item, index) => (
                <button
                  key={item.url}
                  style={{
                    ...recommendStyles.detailThumb,
                    borderColor: galleryIndex === index ? '#1A1A1A' : '#E4DFD6',
                  }}
                  onClick={() => setGalleryIndex(index)}
                >
                  <img src={item.url} alt={item.label} style={recommendStyles.detailThumbImg}/>
                </button>
              ))}
            </div>
          ) : null}
        </div>

        <div style={recommendStyles.detailBlock}>
          <div style={recommendStyles.detailMeta}>
            {mergedProperty.regionName || '—'} · {mergedProperty.districtName || mergedProperty.skcs || mergedProperty.areaName || '—'}
          </div>
          <div style={recommendStyles.detailName}>{mergedProperty.name || '—'}</div>
          <div style={recommendStyles.detailAddress}>{mergedProperty.address || '—'}</div>
        </div>

        <div style={recommendStyles.detailStatsGrid}>
          <DetailStat label="租金范围" value={renderYenWithCny(mergedProperty.detailRentText || mergedProperty.rent || '—')}/>
          <DetailStat label="房型范围" value={mergedProperty.floorPlanText || '—'}/>
          <DetailStat label="当前空房" value={`${mergedProperty.activeRoomCount ?? activeRooms.length} 套`}/>
          <DetailStat label="总户数" value={mergedProperty.householdCount ? `${mergedProperty.householdCount} 户` : '—'}/>
        </div>

        <div style={recommendStyles.detailSection}>
          <div style={recommendStyles.detailSectionTitle}>交通信息</div>
          <div style={recommendStyles.detailBodyText}>{mergedProperty.detailTransportText || mergedProperty.accessText || '—'}</div>
        </div>

        {activeRooms.length > 0 ? (
          <div style={recommendStyles.detailSection}>
            <div style={recommendStyles.detailSectionTitle}>当前可租房间</div>
            <div style={recommendStyles.detailRoomList}>
              {activeRooms.map((room) => (
                <button
                  key={room.roomKey}
                  style={recommendStyles.detailRoomRow}
                  onClick={() => onOpenRoom && onOpenRoom(mergedProperty, room)}
                >
                  <div>
                    <div style={recommendStyles.detailRoomName}>{room.name}</div>
                    <div style={recommendStyles.detailRoomMeta}>
                      {renderYenWithCny(room.rent || '—')} · {room.type || '—'} · {room.floorspace || '—'} · {room.floor || '—'}
                    </div>
                  </div>
                  <Icon.Chevron size={12} dir="right" color="#B0A695"/>
                </button>
              ))}
            </div>
          </div>
        ) : (
          <div style={recommendStyles.detailSection}>
            <div style={recommendStyles.detailSectionTitle}>当前状态</div>
            <div style={recommendStyles.detailBodyText}>当前没有可租房间，可以先了解物业信息，后续再回来查看。</div>
          </div>
        )}
      </div>
    </div>
  );
}

function DetailStat({ label, value }) {
  return (
    <div style={recommendStyles.detailStatCell}>
      <div style={recommendStyles.detailStatLabel}>{label}</div>
      <div style={recommendStyles.detailStatValue}>{value}</div>
    </div>
  );
}

const recommendStyles = {
  shell: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', background: '#FAF8F5', overflow: 'hidden' },
  scroll: { flex: 1, minHeight: 0, overflowY: 'auto', padding: '14px 14px calc(28px + env(safe-area-inset-bottom, 0px))' },
  formCard: { background: '#FFFFFF', borderRadius: 20, border: '1px solid #EDE6D9', padding: '18px 16px', boxShadow: '0 2px 10px rgba(60, 50, 30, 0.04)' },
  resultsCard: { marginTop: 14, background: '#FFFFFF', borderRadius: 20, border: '1px solid #EDE6D9', padding: '18px 16px', boxShadow: '0 2px 10px rgba(60, 50, 30, 0.04)' },
  sectionHead: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 10, marginBottom: 14 },
  sectionTitle: { fontSize: 17, fontWeight: 800, color: '#1A1A1A' },
  mapToggleButton: { flexShrink: 0, minHeight: 34, padding: '0 12px', borderRadius: 999, border: '1px solid #E4DFD6', background: '#FAF6EE', color: '#1A1A1A', fontSize: 12, fontWeight: 800, fontFamily: 'inherit', cursor: 'pointer' },
  formGrid: { display: 'grid', gap: 12 },
  aiField: { display: 'grid', gap: 8 },
  aiTextarea: { width: '100%', minHeight: 128, resize: 'vertical', borderRadius: 16, border: '1px solid #E4DFD6', background: '#FFFCF8', padding: '14px 14px', fontSize: 15, lineHeight: 1.7, color: '#1A1A1A', fontFamily: 'inherit' },
  aiMetaRow: { display: 'flex', justifyContent: 'space-between', gap: 10, color: '#8A7F72', fontSize: 11, fontWeight: 700 },
  exampleRow: { display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 12 },
  exampleChip: { minHeight: 34, padding: '0 12px', borderRadius: 999, border: '1px solid #E4DFD6', background: '#FAF6EE', color: '#6B6258', fontSize: 12, fontWeight: 700, fontFamily: 'inherit', cursor: 'pointer' },
  aiAnswer: { marginBottom: 14, padding: '13px 14px', borderRadius: 16, background: '#FAF6EE', color: '#4F463B', fontSize: 13, lineHeight: 1.8, fontWeight: 600 },
  field: { display: 'grid', gap: 7 },
  fieldLabel: { fontSize: 11, color: '#8A7F72', fontWeight: 800, letterSpacing: '0.06em', textTransform: 'uppercase' },
  contactMethodRow: { display: 'flex', gap: 8, flexWrap: 'wrap' },
  contactMethodChip: { minHeight: 38, padding: '0 14px', borderRadius: 999, border: '1px solid #E4DFD6', background: '#FFFFFF', color: '#6B6258', fontSize: 13, fontWeight: 700, fontFamily: 'inherit', cursor: 'pointer' },
  contactMethodChipActive: { background: '#1A1A1A', borderColor: '#1A1A1A', color: '#FFFFFF' },
  select: { width: '100%', minHeight: 46, borderRadius: 14, border: '1px solid #E4DFD6', background: '#FFFCF8', padding: '0 14px', fontSize: 14, color: '#1A1A1A', fontFamily: 'inherit' },
  input: { width: '100%', minHeight: 46, borderRadius: 14, border: '1px solid #E4DFD6', background: '#FFFCF8', padding: '0 14px', fontSize: 14, color: '#1A1A1A', fontFamily: 'inherit' },
  sliderWrap: { marginTop: 14 },
  sliderCard: { marginTop: 10, borderRadius: 16, border: '1px solid #EDE6D9', background: '#FAF6EE', padding: '16px 14px 14px' },
  sliderValueRow: { display: 'flex', alignItems: 'center', gap: 10, justifyContent: 'space-between' },
  sliderValueBox: { flex: 1 },
  sliderValueLabel: { fontSize: 11, color: '#8A7F72', fontWeight: 700 },
  sliderValueMain: { marginTop: 6, fontSize: 20, fontWeight: 800, color: '#1A1A1A' },
  sliderTrackWrap: { position: 'relative', marginTop: 16, padding: '10px 0' },
  sliderTrackBase: { position: 'absolute', top: 20, left: 0, right: 0, height: 6, borderRadius: 999, background: '#E7DDCF' },
  sliderTrackFill: { position: 'absolute', top: 20, height: 6, borderRadius: 999, background: '#C6572B' },
  rangeInput: { position: 'relative', zIndex: 2, width: '100%', margin: 0, appearance: 'none', background: 'transparent', pointerEvents: 'auto' },
  sliderTickRow: { display: 'flex', justifyContent: 'space-between', marginTop: 10, color: '#8A7F72', fontSize: 11, fontWeight: 600 },
  toggleRow: { marginTop: 14 },
  toggleCard: { display: 'flex', gap: 12, alignItems: 'center', padding: '14px', borderRadius: 16, border: '1px solid #EDE6D9', background: '#FAF6EE' },
  toggleTitle: { fontSize: 14, fontWeight: 700, color: '#1A1A1A' },
  floorPlanWrap: { marginTop: 14 },
  chipGrid: { display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 10 },
  planChip: { minWidth: 54, minHeight: 38, padding: '0 12px', borderRadius: 999, border: '1px solid #E4DFD6', background: '#FFFFFF', color: '#6B6258', fontSize: 13, fontWeight: 700, fontFamily: 'inherit', cursor: 'pointer' },
  planChipActive: { background: '#1A1A1A', borderColor: '#1A1A1A', color: '#FFFFFF' },
  error: { marginTop: 14, padding: '12px 14px', borderRadius: 14, background: '#FFF2EE', color: '#A85D37', fontSize: 12, lineHeight: 1.7 },
  actionRow: { display: 'grid', gap: 10, marginTop: 16 },
  secondaryButton: { width: '100%', minWidth: 0, minHeight: 46, borderRadius: 14, border: '1px solid #E4DFD6', background: '#FFFFFF', color: '#1A1A1A', fontSize: 14, fontWeight: 700, fontFamily: 'inherit', cursor: 'pointer' },
  primaryButton: { width: '100%', minWidth: 0, minHeight: 50, borderRadius: 14, border: 'none', background: '#1A1A1A', color: '#FFFFFF', fontSize: 14, fontWeight: 800, fontFamily: 'inherit', cursor: 'pointer' },
  pendingNotice: { marginBottom: 12, padding: '12px 14px', borderRadius: 14, background: '#FFF8EE', color: '#9A6A2C', fontSize: 12, lineHeight: 1.7 },
  emptyState: { padding: '18px 6px 10px', color: '#8A7F72', fontSize: 13, lineHeight: 1.8 },
  resultList: { display: 'flex', flexDirection: 'column', gap: 12 },
  mapWrap: { display: 'grid', gap: 10 },
  mapCanvas: { width: '100%', height: 420, minHeight: '52vh', borderRadius: 18, overflow: 'hidden', border: '1px solid #EDE6D9', background: '#F4EFE4' },
  mapHint: { color: '#8A7F72', fontSize: 11, lineHeight: 1.6, fontWeight: 600 },
  propertyCard: { display: 'flex', gap: 12, padding: '12px', borderRadius: 18, border: '1px solid #EDE6D9', background: '#FFFCF8' },
  propertyThumb: { width: 84, height: 84, borderRadius: 14, overflow: 'hidden', background: '#F4EFE4', border: '1px solid #EDE6D9', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' },
  propertyThumbImg: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
  propertyBody: { flex: 1, minWidth: 0 },
  propertyTopRow: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 },
  propertyName: { fontSize: 15, fontWeight: 800, color: '#1A1A1A', lineHeight: 1.35 },
  vacancyBadge: { flexShrink: 0, padding: '5px 8px', borderRadius: 999, fontSize: 10, fontWeight: 800 },
  propertyMeta: { marginTop: 4, fontSize: 11, color: '#8A7F72', lineHeight: 1.5 },
  propertyAddress: { marginTop: 6, fontSize: 12, color: '#6B6258', lineHeight: 1.6, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' },
  propertyRent: { marginTop: 8, fontSize: 13, color: '#C6572B', fontWeight: 800 },
  propertySubMeta: { marginTop: 5, fontSize: 11, color: '#8A7F72' },
  tagRow: { display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 10 },
  tag: { display: 'inline-flex', alignItems: 'center', padding: '5px 8px', borderRadius: 999, background: '#F4EFE4', color: '#6B6258', fontSize: 10, fontWeight: 700 },
  propertyActions: { display: 'flex', gap: 8, marginTop: 12 },
  inlineButton: { flex: 1, minHeight: 38, borderRadius: 12, border: 'none', background: '#1A1A1A', color: '#FFFFFF', fontSize: 12, fontWeight: 800, fontFamily: 'inherit', cursor: 'pointer' },
  loadMoreButton: { width: '100%', minHeight: 46, marginTop: 14, borderRadius: 14, border: '1px solid #E4DFD6', background: '#FFFFFF', color: '#1A1A1A', fontSize: 14, fontWeight: 800, fontFamily: 'inherit', cursor: 'pointer' },
  detailShell: { flex: 1, display: 'flex', flexDirection: 'column', background: '#FAF8F5' },
  detailTopBar: { display: 'flex', alignItems: 'center', gap: 10, padding: 'calc(12px + env(safe-area-inset-top, 0px)) 18px 12px', background: '#FAF8F5', borderBottom: '1px solid #EDE6D9' },
  detailBackBtn: { width: 34, height: 34, borderRadius: 17, border: '1px solid #EDE6D9', background: '#FFFFFF', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' },
  detailTopTitle: { minWidth: 0 },
  detailTopMain: { fontSize: 16, fontWeight: 800, color: '#1A1A1A' },
  detailTopSub: { marginTop: 2, fontSize: 12, color: '#8A7F72', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' },
  detailScroll: { flex: 1, overflowY: 'auto', padding: '14px 14px calc(110px + env(safe-area-inset-bottom, 0px))' },
  detailGalleryWrap: { background: '#FFFFFF', borderRadius: 18, border: '1px solid #EDE6D9', padding: '12px', marginBottom: 14 },
  detailGalleryMain: { width: '100%', aspectRatio: '1 / 0.82', borderRadius: 16, overflow: 'hidden', background: '#F4EFE4', display: 'flex', alignItems: 'center', justifyContent: 'center' },
  detailGalleryImg: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
  detailGalleryFallback: { width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' },
  detailThumbRow: { display: 'flex', gap: 8, overflowX: 'auto', paddingTop: 10 },
  detailThumb: { width: 62, height: 62, borderRadius: 12, border: '1px solid #E4DFD6', overflow: 'hidden', background: '#FFFFFF', flexShrink: 0, padding: 0, cursor: 'pointer' },
  detailThumbImg: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
  detailBlock: { background: '#FFFFFF', borderRadius: 18, border: '1px solid #EDE6D9', padding: '18px 16px', marginBottom: 14 },
  detailMeta: { fontSize: 12, color: '#8A7F72', lineHeight: 1.5 },
  detailName: { marginTop: 8, fontSize: 24, fontWeight: 800, color: '#1A1A1A', lineHeight: 1.2 },
  detailAddress: { marginTop: 10, fontSize: 13, color: '#6B6258', lineHeight: 1.7 },
  detailStatsGrid: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 14 },
  detailStatCell: { background: '#FFFFFF', borderRadius: 16, border: '1px solid #EDE6D9', padding: '14px 14px 12px' },
  detailStatLabel: { fontSize: 11, color: '#8A7F72', fontWeight: 700 },
  detailStatValue: { marginTop: 8, fontSize: 14, color: '#1A1A1A', fontWeight: 800, lineHeight: 1.5 },
  detailSection: { background: '#FFFFFF', borderRadius: 18, border: '1px solid #EDE6D9', padding: '16px', marginBottom: 14 },
  detailSectionTitle: { fontSize: 15, fontWeight: 800, color: '#1A1A1A', marginBottom: 10 },
  detailBodyText: { fontSize: 13, color: '#6B6258', lineHeight: 1.8 },
  detailRoomList: { display: 'flex', flexDirection: 'column', gap: 10 },
  detailRoomRow: { width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10, padding: '12px 0', border: 'none', borderTop: '1px dashed #F4EFE4', background: 'transparent', textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit' },
  detailRoomName: { fontSize: 14, fontWeight: 700, color: '#1A1A1A' },
  detailRoomMeta: { marginTop: 4, fontSize: 11, color: '#8A7F72', lineHeight: 1.6 },
};

Object.assign(window, { RecommendScreen });
