import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { apiGet, apiPost, apiPut, apiDelete } from '../../api';
import PullToRefresh from '../../components/PullToRefresh';
import FAB from '../../components/FAB';
import DistrictTierEditor from './components/DistrictTierEditor';
import NotificationSettings from './components/NotificationSettings';
import './Subscription.css';

// ── 상수 ───────────────────────────────────────────────────────────────────────
const STATUS_CONFIG = {
    '청약예정': { color: '#00d4ff', bg: 'rgba(0,212,255,0.1)' },
    '청약중':   { color: '#8b5cf6', bg: 'rgba(139,92,246,0.1)' },
    '결과발표': { color: '#f59e0b', bg: 'rgba(245,158,11,0.1)' },
    '완료':     { color: '#34d399', bg: 'rgba(52,211,153,0.12)' },
};

const HOUSE_TYPE_LABELS = {
    '01': '국민주택',
    '02': '민영주택',
    '03': '도시형생활주택',
};

const TABS = ['대시보드', '공고 목록', '매칭 결과', '내 프로필'];

const STATUS_FILTERS = ['전체', '청약예정', '청약중', '결과발표', '완료'];

const DEFAULT_PROFILE = {
    name: '', age: '', is_homeless: false, is_householder: false,
    subscription_months: '', subscription_amount: '',
    family_members: '', has_dependents: false, children_count: '',
    is_newlywed: false, marriage_months: '',
    has_newborn: false, is_first_home: false, income_level: '',
    preferred_regions: '', preferred_types: '',
    min_area: '', max_area: '', max_price: '',
    // 신규 (자치구 5티어 + 알림 설정)
    preferred_districts: {},
    min_match_score: 70,
    notify_enabled: true,
};

// ── 유틸 ──────────────────────────────────────────────────────────────────────

// 매칭 reasons에서 자치구 티어를 추출 ("자치구 S티어: 강남구 (+25)" → "S")
function extractTier(reasons) {
    for (const r of reasons || []) {
        const m = r.match(/자치구 ([SABCD])티어/);
        if (m) return m[1];
    }
    return null;
}

const fmt = (d) => {
    if (!d) return '-';
    return new Date(d).toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' });
};

const fmtFull = (d) => {
    if (!d) return '-';
    return new Date(d).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' });
};

const _diffDays = (d) => {
    if (!d) return null;
    // 로컬 타임존으로 통일하여 D-day 계산 (UTC 파싱 방지)
    const [y, m, day] = d.split('-').map(Number);
    const target = new Date(y, m - 1, day);
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    return Math.round((target - today) / 86400000);
};

const getDDays = (d) => {
    const diff = _diffDays(d);
    if (diff === null) return null;
    if (diff === 0) return 'D-Day';
    return diff > 0 ? `D-${diff}` : `D+${Math.abs(diff)}`;
};

const getDDayColor = (d) => {
    const diff = _diffDays(d);
    if (diff === null) return 'var(--text-dim)';
    if (diff <= 0) return '#f87171';
    if (diff <= 3) return '#f59e0b';
    if (diff <= 7) return '#00d4ff';
    return 'var(--text-dim)';
};

const fmtDateTime = (d) => {
    if (!d) return '-';
    return new Date(d).toLocaleString('ko-KR', {
        year: 'numeric', month: '2-digit', day: '2-digit',
        hour: '2-digit', minute: '2-digit',
    });
};

async function apiPatch(path, body) {
    const opts = {
        method: 'PATCH',
        headers: { 'Accept': 'application/json' },
    };
    if (body !== undefined) {
        opts.headers['Content-Type'] = 'application/json';
        opts.body = JSON.stringify(body);
    }
    const res = await fetch(path, opts);
    if (!res.ok) {
        const text = await res.text().catch(() => '');
        throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`);
    }
    return res.json();
}

const fmtPrice = (v) => {
    if (v == null) return null;
    if (v >= 10000) return `${(v / 10000).toFixed(v % 10000 === 0 ? 0 : 1)}억`;
    return `${v.toLocaleString()}만`;
};

// ── StatusBadge ──────────────────────────────────────────────────────────────
function StatusBadge({ status, size }) {
    const cfg = STATUS_CONFIG[status] || { color: '#94a3b8', bg: 'rgba(148,163,184,0.1)' };
    const cls = size === 'lg' ? 'sub-badge sub-badge--lg' : 'sub-badge';
    return (
        <span className={cls} style={{ color: cfg.color, background: cfg.bg }}>
            {status || '알 수 없음'}
        </span>
    );
}

// ── DashboardTab ─────────────────────────────────────────────────────────────
function DashboardTab() {
    const [dashboard, setDashboard] = useState(null);
    const [collectStatus, setCollectStatus] = useState(null);
    const [totalCount, setTotalCount] = useState(0);
    const [collecting, setCollecting] = useState(false);
    const [loading, setLoading] = useState(true);

    const load = async () => {
        setLoading(true);
        try {
            const [dash, status, ann] = await Promise.all([
                apiGet('/api/realestate/dashboard'),
                apiGet('/api/realestate/collect/status').catch(() => null),
                apiGet('/api/realestate/announcements?page=1&size=1'),
            ]);
            setDashboard(dash);
            setCollectStatus(status);
            setTotalCount(ann?.total || 0);
        } catch (e) {
            console.error('Dashboard load error:', e);
        } finally {
            setLoading(false);
        }
    };

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

    const handleCollect = async () => {
        setCollecting(true);
        try {
            await apiPost('/api/realestate/collect');
            // Wait a moment then refresh status
            setTimeout(async () => {
                try {
                    const status = await apiGet('/api/realestate/collect/status');
                    setCollectStatus(status);
                } catch (_) {}
                setCollecting(false);
                load();
            }, 3000);
        } catch (e) {
            console.error('Collect error:', e);
            setCollecting(false);
        }
    };

    if (loading) return <div className="sub-empty">불러오는 중...</div>;

    return (
        <div style={{ display: 'grid', gap: 20 }}>
            {/* Stats Cards */}
            <div className="sub-stats-bar">
                <div className="sub-stat-item">
                    <p className="sub-stat-item__value">{dashboard?.active_count ?? 0}</p>
                    <p className="sub-stat-item__label">진행중 공고</p>
                </div>
                <div className="sub-stat-item">
                    <p className="sub-stat-item__value" style={{ color: dashboard?.new_match_count > 0 ? '#f43f5e' : undefined }}>
                        {dashboard?.new_match_count ?? 0}
                    </p>
                    <p className="sub-stat-item__label">신규 매칭</p>
                </div>
                <div className="sub-stat-item">
                    <p className="sub-stat-item__value" style={{ color: dashboard?.bookmarked_count > 0 ? '#f59e0b' : undefined }}>
                        {dashboard?.bookmarked_count ?? 0}
                    </p>
                    <p className="sub-stat-item__label">즐겨찾기</p>
                </div>
                <div className="sub-stat-item">
                    <p className="sub-stat-item__value">{totalCount}</p>
                    <p className="sub-stat-item__label">전체 공고</p>
                </div>
            </div>

            {/* Collection Status */}
            <div className="sub-panel">
                <div className="sub-panel__head">
                    <div>
                        <p className="sub-panel__eyebrow">데이터 수집</p>
                        <h3>공공데이터 수집 현황</h3>
                        {collectStatus && (
                            <p className="sub-panel__sub">
                                마지막 수집: {fmtDateTime(collectStatus.collected_at)}
                                {collectStatus.new_count != null && ` · 신규 ${collectStatus.new_count}건`}
                                {collectStatus.total_count != null && ` · 총 ${collectStatus.total_count}건`}
                                {collectStatus.error && <span style={{ color: '#f87171' }}> · 오류: {collectStatus.error}</span>}
                            </p>
                        )}
                        {!collectStatus && <p className="sub-panel__sub">수집 이력이 없습니다.</p>}
                    </div>
                    <button
                        className="sub-filter-btn is-active"
                        onClick={handleCollect}
                        disabled={collecting}
                        style={{ padding: '8px 20px', fontSize: 13 }}
                    >
                        {collecting ? '수집 중...' : '수집 실행'}
                    </button>
                </div>
            </div>

            {/* Upcoming Schedules */}
            <div className="sub-panel">
                <div className="sub-panel__head">
                    <div>
                        <p className="sub-panel__eyebrow">일정</p>
                        <h3>다가오는 일정</h3>
                    </div>
                </div>
                <div className="sub-panel__body">
                    {dashboard?.upcoming_schedules?.length > 0 ? (
                        <div className="sub-schedule-mini">
                            {dashboard.upcoming_schedules.map((s, i) => {
                                const dday = getDDays(s.date);
                                return (
                                    <div className="sub-schedule-mini__item" key={i}>
                                        <div
                                            className="sub-schedule-mini__dot"
                                            style={{ background: getDDayColor(s.date) }}
                                        />
                                        <div className="sub-schedule-mini__content">
                                            <span className="sub-schedule-mini__label">
                                                {s.house_nm || s.label || '공고'}
                                            </span>
                                            <span className="sub-schedule-mini__date">
                                                {fmtFull(s.date)} · {s.event || s.type || '일정'}
                                            </span>
                                        </div>
                                        {dday && (
                                            <span
                                                className="sub-schedule-mini__dday"
                                                style={{ color: getDDayColor(s.date) }}
                                            >
                                                {dday}
                                            </span>
                                        )}
                                    </div>
                                );
                            })}
                        </div>
                    ) : (
                        <p className="sub-empty-sm">다가오는 일정이 없습니다.</p>
                    )}
                </div>
            </div>

            {/* Bookmarked */}
            {dashboard?.bookmarked?.length > 0 && (
                <div className="sub-panel">
                    <div className="sub-panel__head">
                        <div>
                            <p className="sub-panel__eyebrow">즐겨찾기</p>
                            <h3>관심 공고</h3>
                        </div>
                    </div>
                    <div className="sub-panel__body">
                        <div style={{ display: 'grid', gap: 8 }}>
                            {dashboard.bookmarked.map((item) => {
                                const dday = getDDays(item.receipt_start);
                                const priceText = item.min_price != null
                                    ? (item.min_price === item.max_price_display
                                        ? fmtPrice(item.min_price)
                                        : `${fmtPrice(item.min_price)} ~ ${fmtPrice(item.max_price_display)}`)
                                    : null;
                                return (
                                    <div key={item.id} style={{
                                        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                                        padding: '10px 14px', borderRadius: 'var(--radius-sm)',
                                        border: '1px solid var(--line)', background: 'var(--surface)',
                                    }}>
                                        <div style={{ display: 'grid', gap: 2 }}>
                                            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                                                <span style={{ color: '#f59e0b', fontSize: 14 }}>★</span>
                                                <span style={{ fontWeight: 600, fontSize: 13, color: 'var(--text-bright)' }}>
                                                    {item.house_nm}
                                                </span>
                                                <StatusBadge status={item.status} />
                                            </div>
                                            <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>
                                                {item.region_name || '-'}
                                                {priceText && <> · <span style={{ color: '#f59e0b' }}>{priceText}</span></>}
                                            </span>
                                        </div>
                                        {dday && (
                                            <span style={{ fontSize: 13, fontWeight: 700, color: getDDayColor(item.receipt_start) }}>
                                                {dday}
                                            </span>
                                        )}
                                    </div>
                                );
                            })}
                        </div>
                    </div>
                </div>
            )}
        </div>
    );
}

// ── AnnouncementCard ─────────────────────────────────────────────────────────
function AnnouncementCard({ item, isSelected, onClick, onBookmark }) {
    const dday = getDDays(item.receipt_start);
    const priceText = item.min_price != null
        ? (item.min_price === item.max_price_display
            ? fmtPrice(item.min_price)
            : `${fmtPrice(item.min_price)} ~ ${fmtPrice(item.max_price_display)}`)
        : null;
    return (
        <div
            className={`sub-card${isSelected ? ' is-selected' : ''}`}
            onClick={onClick}
        >
            <div className="sub-card__top">
                <div className="sub-card__badges">
                    <StatusBadge status={item.status} />
                    {item.house_secd && HOUSE_TYPE_LABELS[item.house_secd] && (
                        <span className="sub-type-badge" style={{ color: '#60a5fa' }}>
                            {HOUSE_TYPE_LABELS[item.house_secd]}
                        </span>
                    )}
                    {item.match_score > 0 && (
                        <span className="sub-badge" style={{
                            color: item.match_score >= 70 ? '#34d399' : item.match_score >= 40 ? '#f59e0b' : '#94a3b8',
                            background: item.match_score >= 70 ? 'rgba(52,211,153,0.1)' : item.match_score >= 40 ? 'rgba(245,158,11,0.1)' : 'rgba(148,163,184,0.1)',
                            fontWeight: 700,
                        }}>
                            {item.match_score}점
                        </span>
                    )}
                    {item.district && (
                        <span className="sub-chip sub-chip--district">{item.district}</span>
                    )}
                    {(() => {
                        const tier = extractTier(item.match_reasons);
                        return tier ? (
                            <span className={`sub-chip sub-chip--tier sub-chip--tier-${tier}`}>
                                {tier}티어
                            </span>
                        ) : null;
                    })()}
                </div>
                <button
                    onClick={(e) => { e.stopPropagation(); onBookmark?.(item.id); }}
                    style={{
                        background: 'none', border: 'none', cursor: 'pointer', padding: 2,
                        fontSize: 16, color: item.is_bookmarked ? '#f59e0b' : 'var(--text-dim)',
                        lineHeight: 1,
                    }}
                    title={item.is_bookmarked ? '즐겨찾기 해제' : '즐겨찾기'}
                >
                    {item.is_bookmarked ? '★' : '☆'}
                </button>
            </div>
            <h4 className="sub-card__name">{item.house_nm || '(이름 없음)'}</h4>
            <p className="sub-card__address">{item.address || item.region_name || '-'}</p>
            <div className="sub-card__info">
                <span>{item.total_units ? `${item.total_units}세대` : '-'}</span>
                <span className="sub-card__dot">·</span>
                <span>{item.region_name || '-'}</span>
                {priceText && (
                    <>
                        <span className="sub-card__dot">·</span>
                        <span style={{ color: '#f59e0b' }}>{priceText}</span>
                    </>
                )}
            </div>
            <div className="sub-card__bottom">
                {item.receipt_start && (
                    <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>
                        {fmt(item.receipt_start)} ~ {fmt(item.receipt_end)}
                    </span>
                )}
                {dday && (
                    <span
                        className="sub-card__dday"
                        style={{ color: getDDayColor(item.receipt_start) }}
                    >
                        {dday}
                    </span>
                )}
            </div>
        </div>
    );
}

// ── AnnouncementDetail ───────────────────────────────────────────────────────
function AnnouncementDetail({ item, onBookmark }) {
    const [detailTab, setDetailTab] = useState('info');

    if (!item) {
        return (
            <div className="sub-detail sub-detail--empty">
                <span className="sub-detail__empty-icon">🏠</span>
                <span>공고를 선택하면 상세 정보가 표시됩니다.</span>
            </div>
        );
    }

    return (
        <div className="sub-detail">
            {/* Header */}
            <div className="sub-detail__header">
                <div>
                    <div className="sub-detail__badges">
                        <StatusBadge status={item.status} size="lg" />
                        {item.house_secd && HOUSE_TYPE_LABELS[item.house_secd] && (
                            <span className="sub-type-badge sub-type-badge--lg" style={{ color: '#60a5fa' }}>
                                {HOUSE_TYPE_LABELS[item.house_secd]}
                            </span>
                        )}
                    </div>
                    <h3 className="sub-detail__name">{item.house_nm}</h3>
                    <p className="sub-detail__address">{item.address || item.region_name}</p>
                    {item.match_score > 0 && (
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
                            <span style={{
                                fontSize: 20, fontWeight: 700, fontFamily: 'var(--font-display)',
                                color: item.match_score >= 70 ? '#34d399' : item.match_score >= 40 ? '#f59e0b' : '#94a3b8',
                            }}>
                                매칭 {item.match_score}점
                            </span>
                            {item.eligible_types?.length > 0 && (
                                <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                                    {(Array.isArray(item.eligible_types) ? item.eligible_types : []).map((t, i) => (
                                        <span key={i} className="sub-tag is-neutral" style={{ fontSize: 10 }}>{t}</span>
                                    ))}
                                </div>
                            )}
                        </div>
                    )}
                </div>
                <div className="sub-detail__actions">
                    <button
                        onClick={() => onBookmark?.(item.id)}
                        className="sub-filter-btn"
                        style={{
                            color: item.is_bookmarked ? '#f59e0b' : undefined,
                            borderColor: item.is_bookmarked ? '#f59e0b' : undefined,
                        }}
                    >
                        {item.is_bookmarked ? '★ 즐겨찾기 해제' : '☆ 즐겨찾기'}
                    </button>
                    {item.homepage_url && (
                        <a href={item.homepage_url} target="_blank" rel="noreferrer"
                           className="sub-filter-btn" style={{ textDecoration: 'none' }}>
                            홈페이지
                        </a>
                    )}
                    {item.pblanc_url && (
                        <a href={item.pblanc_url} target="_blank" rel="noreferrer"
                           className="sub-filter-btn" style={{ textDecoration: 'none' }}>
                            공고문
                        </a>
                    )}
                </div>
            </div>

            {/* Section Tabs */}
            <div className="sub-section-tabs">
                <button
                    className={`sub-section-tab${detailTab === 'info' ? ' is-active' : ''}`}
                    onClick={() => setDetailTab('info')}
                >
                    기본 정보
                </button>
                <button
                    className={`sub-section-tab${detailTab === 'schedule' ? ' is-active' : ''}`}
                    onClick={() => setDetailTab('schedule')}
                >
                    일정
                </button>
                {item.models?.length > 0 && (
                    <button
                        className={`sub-section-tab${detailTab === 'models' ? ' is-active' : ''}`}
                        onClick={() => setDetailTab('models')}
                    >
                        주택형 ({item.models.length})
                    </button>
                )}
            </div>

            {/* Section Content */}
            <div className="sub-detail__section-content">
                {detailTab === 'info' && (
                    <div className="sub-compare">
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">단지명</span>
                            <span className="sub-compare__mine" style={{ gridColumn: 'span 2' }}>{item.house_nm}</span>
                        </div>
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">지역</span>
                            <span style={{ gridColumn: 'span 2' }}>{item.region_name || '-'}</span>
                        </div>
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">주소</span>
                            <span style={{ gridColumn: 'span 2' }}>{item.address || '-'}</span>
                        </div>
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">총 세대수</span>
                            <span style={{ gridColumn: 'span 2' }}>{item.total_units ? `${item.total_units}세대` : '-'}</span>
                        </div>
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">시공사</span>
                            <span style={{ gridColumn: 'span 2' }}>{item.constructor || '-'}</span>
                        </div>
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">시행사</span>
                            <span style={{ gridColumn: 'span 2' }}>{item.developer || '-'}</span>
                        </div>
                        <div className="sub-compare__row">
                            <span className="sub-compare__label">입주 예정</span>
                            <span style={{ gridColumn: 'span 2' }}>{item.move_in_month || '-'}</span>
                        </div>
                    </div>
                )}

                {detailTab === 'schedule' && (
                    <div className="sub-schedule-mini">
                        {[
                            { label: '특별공급 접수', start: item.spsply_start, end: item.spsply_end },
                            { label: '1순위 접수', start: item.gnrl_rank1_start, end: item.gnrl_rank1_end },
                            { label: '일반공급 접수', start: item.receipt_start, end: item.receipt_end },
                            { label: '당첨자 발표', start: item.winner_date },
                            { label: '계약', start: item.contract_start, end: item.contract_end },
                        ].filter(s => s.start).map((s, i) => {
                            const dday = getDDays(s.start);
                            return (
                                <div className="sub-schedule-mini__item" key={i}>
                                    <div className="sub-schedule-mini__dot" style={{ background: getDDayColor(s.start) }} />
                                    <div className="sub-schedule-mini__content">
                                        <span className="sub-schedule-mini__label">{s.label}</span>
                                        <span className="sub-schedule-mini__date">
                                            {fmtFull(s.start)}{s.end ? ` ~ ${fmtFull(s.end)}` : ''}
                                        </span>
                                    </div>
                                    {dday && (
                                        <span className="sub-schedule-mini__dday" style={{ color: getDDayColor(s.start) }}>
                                            {dday}
                                        </span>
                                    )}
                                </div>
                            );
                        })}
                        {![item.spsply_start, item.gnrl_rank1_start, item.receipt_start, item.winner_date, item.contract_start].some(Boolean) && (
                            <p className="sub-empty-sm">일정 정보가 없습니다.</p>
                        )}
                    </div>
                )}

                {detailTab === 'models' && item.models?.length > 0 && (
                    <div style={{ display: 'grid', gap: 8 }}>
                        {item.models.map((m, i) => {
                            const totalUnits = (m.general_units || 0) + (m.special_units || 0);
                            return (
                                <div key={i} style={{
                                    border: '1px solid var(--line)',
                                    borderRadius: 'var(--radius-sm)',
                                    padding: '12px 14px',
                                    background: 'var(--surface)',
                                    display: 'grid',
                                    gap: 4,
                                    fontSize: 12,
                                }}>
                                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                                        <span style={{ fontWeight: 600, color: 'var(--text-bright)' }}>
                                            {m.house_ty || `주택형 ${i + 1}`}
                                        </span>
                                        {totalUnits > 0 && (
                                            <span style={{ color: 'var(--text-muted)', fontSize: 11 }}>
                                                {totalUnits}세대
                                            </span>
                                        )}
                                    </div>
                                    {m.supply_area && (
                                        <span style={{ color: 'var(--text-dim)' }}>공급면적 {m.supply_area}m²</span>
                                    )}
                                    {m.top_amount != null && (
                                        <span style={{ color: '#f59e0b', fontWeight: 600 }}>
                                            분양가 {fmtPrice(m.top_amount)}원
                                        </span>
                                    )}
                                </div>
                            );
                        })}
                    </div>
                )}
            </div>

            {item.match_score !== undefined && item.match_score !== null && (
                <div className="sub-match-analysis">
                    <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
                        <div>
                            <p className="sub-panel__eyebrow">매칭 분석</p>
                            <span className="sub-match-analysis__score">
                                ⭐ {item.match_score}<span style={{ fontSize: 14, color: "var(--text-muted)" }}> / 100</span>
                            </span>
                        </div>
                    </div>

                    {item.score_breakdown && (
                        <div style={{ marginTop: 12 }}>
                            <p className="sub-panel__eyebrow" style={{ marginBottom: 8 }}>📊 점수 분석</p>
                            <div style={{ display: 'grid', gap: 8 }}>
                                {[
                                    { key: 'region',      label: '지역',  max: 35, color: '#00d4ff' },
                                    { key: 'type',        label: '유형',  max: 10, color: '#8b5cf6' },
                                    { key: 'area',        label: '면적',  max: 15, color: '#f59e0b' },
                                    { key: 'price',       label: '가격',  max: 15, color: '#f43f5e' },
                                    { key: 'eligibility', label: '자격',  max: 25, color: '#34d399' },
                                ].map(({ key, label, max, color }) => {
                                    const v = item.score_breakdown[key] ?? 0;
                                    return (
                                        <div key={key} style={{ display: 'grid', gap: 3 }}>
                                            <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
                                                <span style={{ color: 'var(--text-bright)', fontWeight: 500 }}>{label}</span>
                                                <span>
                                                    <span style={{ fontWeight: 700, color }}>{v}</span>
                                                    <span style={{ color: 'var(--text-dim)' }}> / {max}</span>
                                                </span>
                                            </div>
                                            <div style={{ height: 5, borderRadius: 3, background: 'var(--surface-raised)', overflow: 'hidden' }}>
                                                <div style={{
                                                    height: '100%', borderRadius: 3, background: color,
                                                    width: `${(v / max) * 100}%`, transition: 'width 0.4s',
                                                }} />
                                            </div>
                                        </div>
                                    );
                                })}
                            </div>
                        </div>
                    )}

                    {item.match_reasons && item.match_reasons.length > 0 && (
                        <div>
                            <p className="sub-panel__eyebrow" style={{ marginTop: 12 }}>💡 매칭 사유</p>
                            <ul className="sub-match-analysis__reasons">
                                {item.match_reasons.map((r, idx) => (
                                    <li key={idx}>{r}</li>
                                ))}
                            </ul>
                        </div>
                    )}

                    {item.eligible_types && item.eligible_types.length > 0 && (
                        <div>
                            <p className="sub-panel__eyebrow" style={{ marginTop: 8 }}>✓ 신청 자격</p>
                            <div className="sub-match-analysis__elig">
                                {item.eligible_types.map(t => (
                                    <span key={t} className="sub-chip">{t}</span>
                                ))}
                            </div>
                        </div>
                    )}
                </div>
            )}
        </div>
    );
}

// ── CalendarView ─────────────────────────────────────────────────────────────
function CalendarView({ items, onDaySelect }) {
    const [cur, setCur] = useState(() => {
        const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), 1);
    });
    const year = cur.getFullYear(), month = cur.getMonth();

    const dateMap = useMemo(() => {
        const map = {};
        for (const item of items) {
            const raw = item.receipt_start || item.spsply_start || item.gnrl_rank1_start;
            if (!raw || raw.length < 8) continue;
            const key = `${raw.slice(0,4)}-${raw.slice(4,6)}-${raw.slice(6,8)}`;
            (map[key] = map[key] || []).push(item);
        }
        return map;
    }, [items]);

    const firstDow = new Date(year, month, 1).getDay();
    const daysInMonth = new Date(year, month + 1, 0).getDate();
    const cells = [];
    for (let i = 0; i < firstDow; i++) cells.push(null);
    for (let d = 1; d <= daysInMonth; d++) cells.push(d);
    while (cells.length % 7 !== 0) cells.push(null);

    const todayD = new Date(), todayKey = `${todayD.getFullYear()}-${String(todayD.getMonth()+1).padStart(2,'0')}-${String(todayD.getDate()).padStart(2,'0')}`;

    return (
        <div className="sub-calendar">
            <div className="sub-calendar__header">
                <button className="sub-filter-btn" onClick={() => setCur(new Date(year, month-1, 1))}>‹</button>
                <span>{year}년 {month+1}월</span>
                <button className="sub-filter-btn" onClick={() => setCur(new Date(year, month+1, 1))}>›</button>
            </div>
            <div className="sub-calendar__weekdays">
                {['일','월','화','수','목','금','토'].map(w => (
                    <div key={w} className="sub-calendar__weekday">{w}</div>
                ))}
            </div>
            <div className="sub-calendar__grid">
                {cells.map((d, i) => {
                    const key = d ? `${year}-${String(month+1).padStart(2,'0')}-${String(d).padStart(2,'0')}` : null;
                    const dayItems = key ? (dateMap[key] || []) : [];
                    const isToday = key === todayKey;
                    return (
                        <div
                            key={i}
                            className={`sub-calendar__day${!d ? ' is-empty' : ''}${isToday ? ' is-today' : ''}${dayItems.length > 0 ? ' has-items' : ''}`}
                            onClick={() => dayItems.length > 0 && onDaySelect(dayItems, `${year}년 ${month+1}월 ${d}일`)}
                        >
                            {d && <span className="sub-calendar__day-num">{d}</span>}
                            {dayItems.length > 0 && (
                                <div className="sub-calendar__dots">
                                    {dayItems.slice(0, 3).map((it, j) => (
                                        <span key={j} className="sub-calendar__dot" style={{ background: STATUS_CONFIG[it.status]?.color || '#888' }} />
                                    ))}
                                    {dayItems.length > 3 && <span className="sub-calendar__more">+{dayItems.length - 3}</span>}
                                </div>
                            )}
                        </div>
                    );
                })}
            </div>
        </div>
    );
}

// ── AnnouncementsTab ─────────────────────────────────────────────────────────
function AnnouncementsTab() {
    const [items, setItems] = useState([]);
    const [total, setTotal] = useState(0);
    const [page, setPage] = useState(1);
    const [statusFilter, setStatusFilter] = useState('전체');
    const [regionFilter, setRegionFilter] = useState('');
    const [bookmarkFilter, setBookmarkFilter] = useState(false);
    const [selected, setSelected] = useState(null);
    const [detail, setDetail] = useState(null);
    const [loading, setLoading] = useState(true);
    const [viewMode, setViewMode] = useState('list'); // 'list' | 'calendar'
    const [calendarDay, setCalendarDay] = useState(null); // { label, items }

    const size = viewMode === 'calendar' ? 200 : 20;

    const load = async () => {
        setLoading(true);
        try {
            const params = new URLSearchParams({ page: String(page), size: String(size) });
            if (statusFilter !== '전체') params.set('status', statusFilter);
            if (regionFilter.trim()) params.set('region', regionFilter.trim());
            if (bookmarkFilter) params.set('bookmarked', 'true');
            const data = await apiGet(`/api/realestate/announcements?${params}`);
            setItems(data.items || []);
            setTotal(data.total || 0);
        } catch (e) {
            console.error('Announcements load error:', e);
            setItems([]);
        } finally {
            setLoading(false);
        }
    };

    useEffect(() => { load(); }, [page, statusFilter, regionFilter, bookmarkFilter, viewMode]);

    const handleSelect = async (item) => {
        setSelected(item.id);
        try {
            const d = await apiGet(`/api/realestate/announcements/${item.id}`);
            setDetail(d);
        } catch (e) {
            console.error('Detail load error:', e);
            setDetail(item);
        }
    };

    const handleDeleteClosed = async () => {
        if (!confirm('종료된(완료) 청약 공고를 모두 삭제할까요?')) return;
        try {
            const res = await apiDelete('/api/realestate/announcements/closed');
            alert(`${res.deleted || 0}건 삭제되었습니다.`);
            setPage(1);
            load();
        } catch (e) {
            console.error('Delete closed error:', e);
            alert('삭제 실패');
        }
    };

    const handleBookmark = async (id) => {
        try {
            const updated = await apiPatch(`/api/realestate/announcements/${id}/bookmark`);
            setItems(prev => prev.map(it =>
                it.id === id ? { ...it, is_bookmarked: updated.is_bookmarked } : it
            ));
            if (detail?.id === id) setDetail(prev => ({ ...prev, is_bookmarked: updated.is_bookmarked }));
        } catch (e) {
            console.error('Bookmark error:', e);
        }
    };

    const totalPages = Math.max(1, Math.ceil(total / size));

    return (
        <div style={{ display: 'grid', gap: 16 }}>
            {/* Filters */}
            <div className="sub-tabs-bar">
                <div className="sub-filter">
                    {STATUS_FILTERS.map((f) => (
                        <button
                            key={f}
                            className={`sub-filter-btn${statusFilter === f ? ' is-active' : ''}`}
                            onClick={() => { setStatusFilter(f); setPage(1); }}
                        >
                            {f}
                        </button>
                    ))}
                </div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                    <button
                        className={`sub-filter-btn${bookmarkFilter ? ' is-active' : ''}`}
                        onClick={() => { setBookmarkFilter(v => !v); setPage(1); }}
                        style={{ fontSize: 12 }}
                    >
                        ★ 즐겨찾기
                    </button>
                    <input
                        className="sub-form-input"
                        placeholder="지역 검색..."
                        value={regionFilter}
                        onChange={(e) => { setRegionFilter(e.target.value); setPage(1); }}
                        style={{ width: 160, padding: '6px 12px', fontSize: 12 }}
                    />
                    <button
                        className={`sub-filter-btn${viewMode === 'calendar' ? ' is-active' : ''}`}
                        onClick={() => { setViewMode(v => v === 'calendar' ? 'list' : 'calendar'); setPage(1); setCalendarDay(null); }}
                        style={{ fontSize: 12 }}
                        title="캘린더 뷰 전환"
                    >
                        📅 캘린더
                    </button>
                    <button
                        className="sub-filter-btn"
                        onClick={handleDeleteClosed}
                        style={{ fontSize: 12, color: '#f87171' }}
                        title="status='완료' 공고 일괄 삭제"
                    >
                        🗑 종료 청약 삭제
                    </button>
                </div>
            </div>

            {loading ? (
                <div className="sub-empty">불러오는 중...</div>
            ) : items.length === 0 ? (
                <div className="sub-empty">조건에 맞는 공고가 없습니다.</div>
            ) : viewMode === 'calendar' ? (
                <div style={{ display: 'grid', gap: 12 }}>
                    <CalendarView
                        items={items}
                        onDaySelect={(dayItems, label) => setCalendarDay({ items: dayItems, label })}
                    />
                    {calendarDay && (
                        <div className="sub-panel">
                            <div className="sub-panel__head">
                                <div>
                                    <p className="sub-panel__eyebrow">{calendarDay.label}</p>
                                    <h3>공고 {calendarDay.items.length}건</h3>
                                </div>
                                <button className="sub-filter-btn" onClick={() => setCalendarDay(null)} style={{ fontSize: 11 }}>닫기</button>
                            </div>
                            <div className="sub-panel__body" style={{ display: 'grid', gap: 8 }}>
                                {calendarDay.items.map(item => (
                                    <div
                                        key={item.id}
                                        className="sub-card"
                                        style={{ cursor: 'pointer', padding: '10px 14px' }}
                                        onClick={() => { setViewMode('list'); handleSelect(item); }}
                                    >
                                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8 }}>
                                            <span style={{ fontWeight: 600, fontSize: 13, color: 'var(--text-bright)' }}>{item.house_nm}</span>
                                            <span className="sub-badge" style={{ background: STATUS_CONFIG[item.status]?.bg, color: STATUS_CONFIG[item.status]?.color, flexShrink: 0 }}>
                                                {item.status}
                                            </span>
                                        </div>
                                        <span style={{ fontSize: 11, color: 'var(--text-dim)' }}>{item.region_name} · 접수 {item.receipt_start}</span>
                                    </div>
                                ))}
                            </div>
                        </div>
                    )}
                </div>
            ) : (
                <div className="sub-list-layout">
                    {/* Card Grid */}
                    <div>
                        <div className="sub-card-grid">
                            {items.map((item) => (
                                <AnnouncementCard
                                    key={item.id}
                                    item={item}
                                    isSelected={selected === item.id}
                                    onClick={() => handleSelect(item)}
                                    onBookmark={handleBookmark}
                                />
                            ))}
                        </div>

                        {/* Pagination */}
                        {totalPages > 1 && (
                            <div style={{ display: 'flex', justifyContent: 'center', gap: 8, marginTop: 16 }}>
                                <button
                                    className="sub-filter-btn"
                                    disabled={page <= 1}
                                    onClick={() => setPage(p => p - 1)}
                                >
                                    ‹ 이전
                                </button>
                                <span style={{ fontSize: 13, color: 'var(--text-dim)', padding: '4px 8px' }}>
                                    {page} / {totalPages}
                                </span>
                                <button
                                    className="sub-filter-btn"
                                    disabled={page >= totalPages}
                                    onClick={() => setPage(p => p + 1)}
                                >
                                    다음 ›
                                </button>
                            </div>
                        )}
                    </div>

                    {/* Detail Panel */}
                    <div className="sub-detail-panel">
                        <AnnouncementDetail item={detail} onBookmark={handleBookmark} />
                    </div>
                </div>
            )}
        </div>
    );
}

// ── MatchesTab ────────────────────────────────────────────────────────────────
function MatchesTab() {
    const [items, setItems] = useState([]);
    const [total, setTotal] = useState(0);
    const [myPoints, setMyPoints] = useState(null);
    const [page, setPage] = useState(1);
    const [refreshing, setRefreshing] = useState(false);
    const [loading, setLoading] = useState(true);

    const size = 20;

    const load = async () => {
        setLoading(true);
        try {
            const data = await apiGet(`/api/realestate/matches?page=${page}&size=${size}`);
            setItems(data.items || []);
            setTotal(data.total || 0);
            if (data.my_points) setMyPoints(data.my_points);
        } catch (e) {
            console.error('Matches load error:', e);
            setItems([]);
        } finally {
            setLoading(false);
        }
    };

    useEffect(() => { load(); }, [page]);

    const handleRefresh = async () => {
        setRefreshing(true);
        try {
            await apiPost('/api/realestate/matches/refresh');
            await load();
        } catch (e) {
            console.error('Refresh error:', e);
        } finally {
            setRefreshing(false);
        }
    };

    const handleMarkRead = async (id) => {
        try {
            await apiPatch(`/api/realestate/matches/${id}/read`);
            setItems(prev => prev.map(m => m.id === id ? { ...m, is_new: false } : m));
        } catch (e) {
            console.error('Mark read error:', e);
        }
    };

    const totalPages = Math.max(1, Math.ceil(total / size));

    return (
        <div style={{ display: 'grid', gap: 16 }}>
            <div className="sub-tabs-bar">
                <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                    <p style={{ margin: 0, fontSize: 13, color: 'var(--text-dim)' }}>
                        총 {total}건의 매칭 결과
                    </p>
                    {myPoints && (
                        <span className="sub-badge" style={{
                            color: myPoints.total >= 60 ? '#34d399' : myPoints.total >= 40 ? '#f59e0b' : '#f87171',
                            background: myPoints.total >= 60 ? 'rgba(52,211,153,0.1)' : myPoints.total >= 40 ? 'rgba(245,158,11,0.1)' : 'rgba(248,113,113,0.1)',
                            fontWeight: 700, fontSize: 12,
                        }}>
                            내 가점 {myPoints.total}/{myPoints.max_total}
                        </span>
                    )}
                </div>
                <button
                    className="sub-filter-btn is-active"
                    onClick={handleRefresh}
                    disabled={refreshing}
                    style={{ padding: '8px 20px', fontSize: 13 }}
                >
                    {refreshing ? '재계산 중...' : '재계산'}
                </button>
            </div>

            {loading ? (
                <div className="sub-empty">불러오는 중...</div>
            ) : items.length === 0 ? (
                <div className="sub-empty">
                    매칭 결과가 없습니다. 프로필을 설정하고 재계산을 실행해 보세요.
                </div>
            ) : (
                <>
                    <div style={{ display: 'grid', gap: 10 }}>
                        {items.map((match) => (
                            <div
                                key={match.id}
                                className="sub-card"
                                style={{
                                    gridTemplateColumns: '1fr auto',
                                    alignItems: 'center',
                                    borderLeft: match.is_new ? '3px solid #f43f5e' : undefined,
                                    cursor: match.is_new ? 'pointer' : 'default',
                                }}
                                onClick={() => match.is_new && handleMarkRead(match.id)}
                            >
                                <div style={{ display: 'grid', gap: 6 }}>
                                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                                        <h4 className="sub-card__name" style={{ margin: 0 }}>
                                            {match.house_nm || `공고 #${match.announcement_id}`}
                                        </h4>
                                        {match.is_new && (
                                            <span className="sub-badge" style={{ color: '#f43f5e', background: 'rgba(244,63,94,0.1)' }}>
                                                NEW
                                            </span>
                                        )}
                                        {match.ann_status && <StatusBadge status={match.ann_status} />}
                                        {match.district && (
                                            <span className="sub-chip sub-chip--district">{match.district}</span>
                                        )}
                                        {(() => {
                                            const tier = extractTier(match.match_reasons);
                                            return tier ? (
                                                <span className={`sub-chip sub-chip--tier sub-chip--tier-${tier}`}>
                                                    {tier}티어
                                                </span>
                                            ) : null;
                                        })()}
                                    </div>
                                    <p className="sub-card__address" style={{ margin: 0 }}>
                                        {match.region_name || '-'}
                                        {match.receipt_start && (
                                            <span style={{ marginLeft: 8 }}>
                                                {fmt(match.receipt_start)} ~ {fmt(match.receipt_end)}
                                                {(() => {
                                                    const dd = getDDays(match.receipt_start);
                                                    return dd ? <span style={{ marginLeft: 6, fontWeight: 600, color: getDDayColor(match.receipt_start) }}>{dd}</span> : null;
                                                })()}
                                            </span>
                                        )}
                                    </p>
                                    {match.eligible_types && (
                                        <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                                            {(Array.isArray(match.eligible_types)
                                                ? match.eligible_types
                                                : (() => { try { return JSON.parse(match.eligible_types); } catch { return []; } })()
                                            ).map((t, i) => (
                                                <span key={i} className="sub-tag is-neutral" style={{ fontSize: 10 }}>
                                                    {t}
                                                </span>
                                            ))}
                                        </div>
                                    )}
                                    {match.match_reasons?.length > 0 && (
                                        <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>
                                            {(Array.isArray(match.match_reasons)
                                                ? match.match_reasons
                                                : (() => { try { return JSON.parse(match.match_reasons); } catch { return []; } })()
                                            ).join(' · ')}
                                        </div>
                                    )}
                                    {match.score_breakdown && (
                                        <div style={{ display: 'flex', gap: 2, marginTop: 4 }}>
                                            {[
                                                { key: 'region',      max: 35, color: '#00d4ff' },
                                                { key: 'type',        max: 10, color: '#8b5cf6' },
                                                { key: 'area',        max: 15, color: '#f59e0b' },
                                                { key: 'price',       max: 15, color: '#f43f5e' },
                                                { key: 'eligibility', max: 25, color: '#34d399' },
                                            ].map(({ key, max, color }) => {
                                                const v = match.score_breakdown[key] ?? 0;
                                                return (
                                                    <div key={key} style={{ flex: max, height: 4, borderRadius: 2, background: 'var(--surface-raised)', overflow: 'hidden' }}>
                                                        <div style={{ height: '100%', borderRadius: 2, background: color, width: `${(v / max) * 100}%` }} />
                                                    </div>
                                                );
                                            })}
                                        </div>
                                    )}
                                </div>
                                <div style={{ textAlign: 'center', flexShrink: 0, display: 'grid', gap: 6 }}>
                                    <div>
                                        <div style={{
                                            fontFamily: 'var(--font-display)',
                                            fontSize: 28,
                                            fontWeight: 700,
                                            color: (match.match_score ?? 0) >= 70 ? '#34d399' : (match.match_score ?? 0) >= 40 ? '#f59e0b' : '#f87171',
                                            lineHeight: 1,
                                        }}>
                                            {match.match_score ?? '-'}
                                        </div>
                                        <div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 4 }}>
                                            매칭 점수
                                        </div>
                                    </div>
                                    {myPoints && (
                                        <div style={{
                                            fontSize: 11, padding: '3px 8px', borderRadius: 4,
                                            background: myPoints.total >= 50 ? 'rgba(52,211,153,0.1)' : 'rgba(248,113,113,0.1)',
                                            color: myPoints.total >= 50 ? '#34d399' : '#f87171',
                                            fontWeight: 600, whiteSpace: 'nowrap',
                                        }}>
                                            가점 {myPoints.total}점
                                        </div>
                                    )}
                                </div>
                            </div>
                        ))}
                    </div>

                    {totalPages > 1 && (
                        <div style={{ display: 'flex', justifyContent: 'center', gap: 8, marginTop: 8 }}>
                            <button className="sub-filter-btn" disabled={page <= 1} onClick={() => setPage(p => p - 1)}>
                                ‹ 이전
                            </button>
                            <span style={{ fontSize: 13, color: 'var(--text-dim)', padding: '4px 8px' }}>
                                {page} / {totalPages}
                            </span>
                            <button className="sub-filter-btn" disabled={page >= totalPages} onClick={() => setPage(p => p + 1)}>
                                다음 ›
                            </button>
                        </div>
                    )}
                </>
            )}
        </div>
    );
}

// ── ProfileTab ────────────────────────────────────────────────────────────────
function ProfileTab() {
    const [profile, setProfile] = useState({ ...DEFAULT_PROFILE });
    const [passCount, setPassCount] = useState(null);
    const [saving, setSaving] = useState(false);
    const [loading, setLoading] = useState(true);
    const [message, setMessage] = useState('');

    useEffect(() => {
        (async () => {
            setLoading(true);
            try {
                const [data, dash] = await Promise.all([
                    apiGet('/api/realestate/profile'),
                    apiGet('/api/realestate/dashboard').catch(() => null),
                ]);
                if (data && Object.keys(data).length > 0) {
                    const display = { ...DEFAULT_PROFILE, ...data };
                    if (Array.isArray(display.preferred_regions)) display.preferred_regions = display.preferred_regions.join(', ');
                    if (Array.isArray(display.preferred_types)) display.preferred_types = display.preferred_types.join(', ');
                    setProfile(display);
                }
                if (dash?.pass_count != null) setPassCount(dash.pass_count);
            } catch (e) {
                console.error('Profile load error:', e);
            } finally {
                setLoading(false);
            }
        })();
    }, []);

    const handleChange = (key, value) => {
        setProfile(prev => ({ ...prev, [key]: value }));
    };

    const handleCheckbox = (key) => {
        setProfile(prev => ({ ...prev, [key]: !prev[key] }));
    };

    const handleSave = async () => {
        setSaving(true);
        setMessage('');
        try {
            const payload = { ...profile };
            // Convert numeric strings to numbers
            ['age', 'subscription_months', 'subscription_amount', 'family_members',
             'children_count', 'marriage_months', 'min_area', 'max_area', 'max_price'
            ].forEach(k => {
                if (payload[k] !== '' && payload[k] != null) {
                    payload[k] = Number(payload[k]);
                } else {
                    payload[k] = null;
                }
            });
            // Convert comma-separated strings to arrays
            payload.preferred_regions = typeof payload.preferred_regions === 'string'
                ? payload.preferred_regions.split(',').map(s => s.trim()).filter(Boolean)
                : (payload.preferred_regions || []);
            payload.preferred_types = typeof payload.preferred_types === 'string'
                ? payload.preferred_types.split(',').map(s => s.trim()).filter(Boolean)
                : (payload.preferred_types || []);
            // Send empty arrays as null
            if (payload.preferred_regions.length === 0) payload.preferred_regions = null;
            if (payload.preferred_types.length === 0) payload.preferred_types = null;

            // 신규: preferred_districts (객체), min_match_score, notify_enabled
            payload.preferred_districts = profile.preferred_districts && typeof profile.preferred_districts === "object"
                ? profile.preferred_districts
                : {};
            payload.min_match_score = profile.min_match_score ?? null;
            payload.notify_enabled = profile.notify_enabled ?? null;

            const updated = await apiPut('/api/realestate/profile', payload);
            if (updated && Object.keys(updated).length > 0) {
                // Convert arrays back to comma-separated strings for display
                const display = { ...DEFAULT_PROFILE, ...updated };
                if (Array.isArray(display.preferred_regions)) display.preferred_regions = display.preferred_regions.join(', ');
                if (Array.isArray(display.preferred_types)) display.preferred_types = display.preferred_types.join(', ');
                setProfile(display);
            }
            setMessage('저장 완료');
            setTimeout(() => setMessage(''), 2000);
        } catch (e) {
            console.error('Profile save error:', e);
            setMessage('저장 실패: ' + e.message);
        } finally {
            setSaving(false);
        }
    };

    if (loading) return <div className="sub-empty">불러오는 중...</div>;

    const pts = profile.subscription_points;

    return (
        <div style={{ display: 'grid', gap: 16 }}>
            {/* 가점 카드 */}
            {pts && pts.total > 0 && (
                <div className="sub-panel">
                    <div className="sub-panel__head">
                        <div>
                            <p className="sub-panel__eyebrow">청약 가점</p>
                            <h3>내 가점 현황</h3>
                        </div>
                        <div style={{
                            fontFamily: 'var(--font-display)', fontSize: 36, fontWeight: 700,
                            color: pts.total >= 60 ? '#34d399' : pts.total >= 40 ? '#f59e0b' : '#f87171',
                            lineHeight: 1,
                        }}>
                            {pts.total}<span style={{ fontSize: 16, color: 'var(--text-muted)', fontWeight: 400 }}> / {pts.max_total}</span>
                        </div>
                    </div>
                    <div className="sub-panel__body" style={{ display: 'grid', gap: 12 }}>
                        {[
                            { label: '무주택기간', data: pts.homeless_duration, color: '#00d4ff' },
                            { label: '부양가족 수', data: pts.dependents, color: '#8b5cf6' },
                            { label: '청약통장 가입기간', data: pts.subscription_period, color: '#f59e0b' },
                        ].map(({ label, data, color }) => (
                            <div key={label} style={{ display: 'grid', gap: 4 }}>
                                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13 }}>
                                    <span style={{ color: 'var(--text-bright)', fontWeight: 500 }}>{label}</span>
                                    <span>
                                        <span style={{ fontWeight: 700, color }}>{data.score}</span>
                                        <span style={{ color: 'var(--text-dim)' }}> / {data.max}</span>
                                    </span>
                                </div>
                                <div style={{ height: 6, borderRadius: 3, background: 'var(--surface-raised)', overflow: 'hidden' }}>
                                    <div style={{
                                        height: '100%', borderRadius: 3, background: color,
                                        width: `${(data.score / data.max) * 100}%`, transition: 'width 0.3s',
                                    }} />
                                </div>
                                <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>{data.detail}</span>
                            </div>
                        ))}
                    </div>
                </div>
            )}

        <div className="sub-panel">
            <div className="sub-panel__head">
                <div>
                    <p className="sub-panel__eyebrow">프로필</p>
                    <h3>내 청약 프로필</h3>
                    <p className="sub-panel__sub">자격 조건과 선호 조건을 설정하면 공고 매칭에 활용됩니다. <span style={{ color: '#f43f5e', fontSize: 11 }}>* 필수 입력</span></p>
                </div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                    {message && (
                        <span style={{
                            fontSize: 12,
                            color: message.startsWith('저장 완료') ? '#34d399' : '#f87171',
                        }}>
                            {message}
                        </span>
                    )}
                    <button
                        className="sub-filter-btn is-active"
                        onClick={handleSave}
                        disabled={saving}
                        style={{ padding: '8px 20px', fontSize: 13 }}
                    >
                        {saving ? '저장 중...' : '저장'}
                    </button>
                </div>
            </div>

            {/* 프로필 완성도 힌트 */}
            {(() => {
                const missing = [];
                if (!profile.income_level) missing.push('소득 수준');
                if (!profile.min_area || !profile.max_area) missing.push('희망 면적');
                if (!profile.max_price) missing.push('최대 예산');
                const hasDistricts = profile.preferred_districts &&
                    Object.values(profile.preferred_districts).some(arr => arr?.length > 0);
                if (!hasDistricts) missing.push('자치구 티어');
                if (missing.length === 0) return null;
                return (
                    <div className="sub-profile-hint">
                        <span className="sub-profile-hint__icon">💡</span>
                        <span>
                            <strong>매칭 정확도 개선 가능</strong> — {missing.join(', ')} 입력 시 더 정확한 점수를 산출합니다.
                        </span>
                    </div>
                );
            })()}

            <div className="sub-modal__form">
                {/* 기본 정보 */}
                <div className="sub-form-section" style={{ borderBottom: '1px solid var(--line)' }}>
                    <p className="sub-form-section__title">기본 정보</p>
                    <div className="sub-form-row">
                        <label className="sub-form-label">
                            이름
                            <input
                                className="sub-form-input"
                                value={profile.name || ''}
                                onChange={e => handleChange('name', e.target.value)}
                                placeholder="이름"
                            />
                        </label>
                        <label className="sub-form-label">
                            나이
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.age || ''}
                                onChange={e => handleChange('age', e.target.value)}
                                placeholder="만 나이"
                            />
                        </label>
                    </div>
                </div>

                {/* 자격 조건 */}
                <div className="sub-form-section" style={{ borderBottom: '1px solid var(--line)' }}>
                    <p className="sub-form-section__title">자격 조건</p>

                    <div className="sub-form-checks">
                        <label className="sub-form-check">
                            <input type="checkbox" checked={!!profile.is_homeless} onChange={() => handleCheckbox('is_homeless')} />
                            무주택자 *
                        </label>
                        <label className="sub-form-check">
                            <input type="checkbox" checked={!!profile.is_householder} onChange={() => handleCheckbox('is_householder')} />
                            세대주 *
                        </label>
                        <label className="sub-form-check">
                            <input type="checkbox" checked={!!profile.has_dependents} onChange={() => handleCheckbox('has_dependents')} />
                            부양가족 있음
                        </label>
                        <label className="sub-form-check">
                            <input type="checkbox" checked={!!profile.is_newlywed} onChange={() => handleCheckbox('is_newlywed')} />
                            신혼부부
                        </label>
                        <label className="sub-form-check">
                            <input type="checkbox" checked={!!profile.has_newborn} onChange={() => handleCheckbox('has_newborn')} />
                            출산/입양
                        </label>
                        <label className="sub-form-check">
                            <input type="checkbox" checked={!!profile.is_first_home} onChange={() => handleCheckbox('is_first_home')} />
                            생애최초
                        </label>
                    </div>

                    <div className="sub-form-row--three" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
                        <label className="sub-form-label">
                            청약 납입 기간 (개월) *
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.subscription_months || ''}
                                onChange={e => handleChange('subscription_months', e.target.value)}
                                placeholder="예: 84"
                            />
                        </label>
                        <label className="sub-form-label">
                            청약 납입 금액 (만원)
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.subscription_amount || ''}
                                onChange={e => handleChange('subscription_amount', e.target.value)}
                                placeholder="예: 1500"
                            />
                        </label>
                        <label className="sub-form-label">
                            가족 수 *
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.family_members || ''}
                                onChange={e => handleChange('family_members', e.target.value)}
                                placeholder="본인 포함"
                            />
                        </label>
                    </div>

                    <div className="sub-form-row--three" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
                        <label className="sub-form-label">
                            자녀 수
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.children_count || ''}
                                onChange={e => handleChange('children_count', e.target.value)}
                                placeholder="0"
                            />
                        </label>
                        <label className="sub-form-label">
                            혼인 기간 (개월)
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.marriage_months || ''}
                                onChange={e => handleChange('marriage_months', e.target.value)}
                                placeholder="미혼이면 비워두세요"
                            />
                        </label>
                        <label className="sub-form-label">
                            소득 수준 (%)
                            <input
                                className="sub-form-input"
                                type="number"
                                min="0"
                                max="300"
                                value={profile.income_level || ''}
                                onChange={e => handleChange('income_level', e.target.value)}
                                placeholder="도시근로자 월평균 대비 %"
                            />
                            <span className="sub-form-hint">청년 ≤140 / 신혼·생애최초 ≤160 / 신생아 ≤200 · 미입력 시 검증 생략</span>
                        </label>
                    </div>
                </div>

                {/* 선호 조건 */}
                <div className="sub-form-section">
                    <p className="sub-form-section__title">선호 조건</p>

                    <div className="sub-form-row">
                        <label className="sub-form-label">
                            선호 지역 *
                            <input
                                className="sub-form-input"
                                value={profile.preferred_regions || ''}
                                onChange={e => handleChange('preferred_regions', e.target.value)}
                                placeholder="예: 서울, 경기"
                            />
                        </label>
                        <label className="sub-form-label">
                            선호 주택유형
                            <input
                                className="sub-form-input"
                                value={profile.preferred_types || ''}
                                onChange={e => handleChange('preferred_types', e.target.value)}
                                placeholder="예: 국민주택, 민영주택"
                            />
                        </label>
                    </div>

                    <div className="sub-form-row--three" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
                        <label className="sub-form-label">
                            최소 면적 (m²)
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.min_area || ''}
                                onChange={e => handleChange('min_area', e.target.value)}
                                placeholder="예: 59"
                            />
                        </label>
                        <label className="sub-form-label">
                            최대 면적 (m²)
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.max_area || ''}
                                onChange={e => handleChange('max_area', e.target.value)}
                                placeholder="예: 84"
                            />
                        </label>
                        <label className="sub-form-label">
                            최대 분양가 (만원)
                            <input
                                className="sub-form-input"
                                type="number"
                                value={profile.max_price || ''}
                                onChange={e => handleChange('max_price', e.target.value)}
                                placeholder="예: 80000"
                            />
                        </label>
                    </div>
                </div>

                {/* 자치구 5티어 */}
                <DistrictTierEditor
                    value={profile.preferred_districts}
                    onChange={(next) => setProfile(prev => ({ ...prev, preferred_districts: next }))}
                />

                {/* 알림 설정 */}
                <NotificationSettings
                    minScore={profile.min_match_score ?? 70}
                    notifyEnabled={profile.notify_enabled ?? true}
                    onChange={(patch) => setProfile(prev => ({ ...prev, ...patch }))}
                    passCount={passCount}
                />
            </div>
        </div>
        </div>
    );
}

// ── Subscription (Main) ──────────────────────────────────────────────────────
function Subscription() {
    const [activeTab, setActiveTab] = useState(0);
    const [refreshKey, setRefreshKey] = useState(0);

    const handleRefresh = useCallback(async () => {
        setRefreshKey(k => k + 1);
    }, []);

    const handleFABClick = useCallback(() => {
        setActiveTab(1); // 공고 목록 탭으로 이동
    }, []);

    return (
        <PullToRefresh onRefresh={handleRefresh}>
        <div className="sub">
            {/* Header */}
            <div className="sub-header">
                <div>
                    <p className="sub-kicker">Real Estate</p>
                    <h1>청약 관리</h1>
                    <p className="sub-desc">
                        공공데이터 기반 청약 공고 자동 수집, 내 조건 매칭, 일정 관리를 한곳에서.
                    </p>
                </div>
            </div>

            {/* Tabs */}
            <div className="sub-tabs-bar">
                <div className="sub-tabs">
                    {TABS.map((tab, i) => (
                        <button
                            key={tab}
                            className={`sub-tab${activeTab === i ? ' is-active' : ''}`}
                            onClick={() => setActiveTab(i)}
                        >
                            {tab}
                        </button>
                    ))}
                </div>
            </div>

            {/* Body */}
            <div className="sub-body">
                {activeTab === 0 && <DashboardTab key={`dash-${refreshKey}`} />}
                {activeTab === 1 && <AnnouncementsTab key={`ann-${refreshKey}`} />}
                {activeTab === 2 && <MatchesTab key={`match-${refreshKey}`} />}
                {activeTab === 3 && <ProfileTab key={`prof-${refreshKey}`} />}
            </div>

            <FAB onClick={handleFABClick} label="공고 목록" />
        </div>
        </PullToRefresh>
    );
}

export default Subscription;
