import { useState, useMemo, useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import {
    batchApi,
    type RecollectionHistoryDto,
    type RecollectionSearchResponse,
    type CollectionPeriodDto,
    type LastCollectionStatusDto,
    type JobDisplayName,
} from '../api/batchApi';
import { formatDateTime, formatDuration } from '../utils/formatters';
import { usePoller } from '../hooks/usePoller';
import { useToastContext } from '../contexts/ToastContext';
import StatusBadge from '../components/StatusBadge';
import InfoModal from '../components/InfoModal';
import EmptyState from '../components/EmptyState';
import LoadingSpinner from '../components/LoadingSpinner';
import GuideModal, { HelpButton } from '../components/GuideModal';

type StatusFilter = 'ALL' | 'COMPLETED' | 'FAILED' | 'STARTED';

const STATUS_FILTERS: { value: StatusFilter; label: string }[] = [
    { value: 'ALL', label: '전체' },
    { value: 'COMPLETED', label: '완료' },
    { value: 'FAILED', label: '실패' },
    { value: 'STARTED', label: '실행중' },
];

const POLLING_INTERVAL_MS = 10_000;
const PAGE_SIZE = 20;

/** datetime 문자열에서 date input용 값 추출 (YYYY-MM-DD) */
function toDateInput(dt: string | null): string {
    if (!dt) return '';
    return dt.substring(0, 10);
}

/** datetime 문자열에서 time input용 값 추출 (HH:mm) */
function toTimeInput(dt: string | null): string {
    if (!dt) return '00:00';
    const t = dt.substring(11, 16);
    return t || '00:00';
}

/** date + time을 ISO datetime 문자열로 결합 */
function toIsoDateTime(date: string, time: string): string {
    return `${date}T${time || '00:00'}:00`;
}

interface PeriodEdit {
    fromDate: string;
    fromTime: string;
    toDate: string;
    toTime: string;
}

// ── 수집 상태 임계값 (분 단위) ────────────────────────────────
const THRESHOLD_WARN_MINUTES = 24 * 60;   // 24시간
const THRESHOLD_DANGER_MINUTES = 48 * 60; // 48시간

type CollectionStatusLevel = 'normal' | 'warn' | 'danger';

function getStatusLevel(elapsedMinutes: number): CollectionStatusLevel {
    if (elapsedMinutes < 0) return 'danger';
    if (elapsedMinutes <= THRESHOLD_WARN_MINUTES) return 'normal';
    if (elapsedMinutes <= THRESHOLD_DANGER_MINUTES) return 'warn';
    return 'danger';
}

function getStatusLabel(level: CollectionStatusLevel): string {
    if (level === 'normal') return '정상';
    if (level === 'warn') return '주의';
    return '경고';
}

function getStatusColor(level: CollectionStatusLevel): string {
    if (level === 'normal') return 'text-green-500';
    if (level === 'warn') return 'text-yellow-500';
    return 'text-red-500';
}

function getStatusBgColor(level: CollectionStatusLevel): string {
    if (level === 'normal') return 'bg-green-500/10 text-green-600 dark:text-green-400';
    if (level === 'warn') return 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400';
    return 'bg-red-500/10 text-red-600 dark:text-red-400';
}

function formatElapsed(minutes: number): string {
    if (minutes < 0) return '-';
    if (minutes < 60) return `${minutes}분 전`;
    const hours = Math.floor(minutes / 60);
    if (hours < 24) return `${hours}시간 ${minutes % 60}분 전`;
    const days = Math.floor(hours / 24);
    const remainHours = hours % 24;
    return remainHours > 0 ? `${days}일 ${remainHours}시간 전` : `${days}일 전`;
}

/** 기간 프리셋 정의 (시간 단위) */
const DURATION_PRESETS = [
    { label: '6시간', hours: 6 },
    { label: '12시간', hours: 12 },
    { label: '하루', hours: 24 },
    { label: '일주일', hours: 168 },
] as const;

/** 시작 날짜+시간에 시간(hours)을 더해 종료 날짜+시간을 반환 */
function addHoursToDateTime(
    date: string,
    time: string,
    hours: number,
): { toDate: string; toTime: string } {
    if (!date) return { toDate: '', toTime: '00:00' };
    const dt = new Date(`${date}T${time || '00:00'}:00`);
    dt.setTime(dt.getTime() + hours * 60 * 60 * 1000);
    const y = dt.getFullYear();
    const m = String(dt.getMonth() + 1).padStart(2, '0');
    const d = String(dt.getDate()).padStart(2, '0');
    const hh = String(dt.getHours()).padStart(2, '0');
    const mm = String(dt.getMinutes()).padStart(2, '0');
    return { toDate: `${y}-${m}-${d}`, toTime: `${hh}:${mm}` };
}

export default function Recollects() {
    const navigate = useNavigate();
    const { showToast } = useToastContext();
    const [displayNames, setDisplayNames] = useState<JobDisplayName[]>([]);
    const [lastCollectionStatuses, setLastCollectionStatuses] = useState<LastCollectionStatusDto[]>([]);
    const [lastCollectionPanelOpen, setLastCollectionPanelOpen] = useState(false);

    const [periods, setPeriods] = useState<CollectionPeriodDto[]>([]);
    const [histories, setHistories] = useState<RecollectionHistoryDto[]>([]);
    const [selectedApiKey, setSelectedApiKey] = useState('');
    const [apiDropdownOpen, setApiDropdownOpen] = useState(false);
    const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');
    const [loading, setLoading] = useState(true);

    // 날짜 범위 필터 + 페이지네이션
    const [startDate, setStartDate] = useState('');
    const [endDate, setEndDate] = useState('');
    const [page, setPage] = useState(0);
    const [totalPages, setTotalPages] = useState(0);
    const [totalCount, setTotalCount] = useState(0);
    const [useSearch, setUseSearch] = useState(false);

    // 실패건 수 (jobExecutionId → count)
    const [failedRecordCounts, setFailedRecordCounts] = useState<Record<number, number>>({});

    // 실패 로그 모달
    const [failLogTarget, setFailLogTarget] = useState<RecollectionHistoryDto | null>(null);

    // 가이드 모달
    const [guideOpen, setGuideOpen] = useState(false);

const RECOLLECTS_GUIDE = [
    {
        title: '재수집이란?',
        content: '재수집은 특정 기간의 데이터를 다시 수집하는 기능입니다.\n수집 누락이나 데이터 오류가 발생했을 때 사용합니다.\n자동 재수집은 시스템이 실패 건을 자동으로 재시도하며, 수동 재수집은 사용자가 직접 요청합니다.',
    },
    {
      title: '마지막 수집 완료 일시',
      content: '각 API별 마지막 수집 완료 일시를 보여줍니다.\n이 정보를 참고하여 재수집이 필요한 기간을 판단할 수 있습니다.',
    },
    {
        title: '재수집 기간 관리',
        content: '1. "재수집 기간 관리" 영역에서 수집할 작업을 선택합니다\n2. 수집 기간(시작~종료)을 설정합니다\n3. 재수집 사유를 입력합니다 (선택)\n4. "재수집 요청" 버튼을 클릭합니다\n\n기간 설정 시 기존 수집 기간과 중복되면 경고가 표시됩니다.',
    },
    {
        title: '이력 조회',
        content: '작업 선택, 상태 필터, 날짜 범위로 재수집 이력을 검색할 수 있습니다.\n상태: 완료(COMPLETED) / 실패(FAILED) / 실행중(STARTED)\n각 행을 클릭하면 상세 화면으로 이동합니다.',
    },
];

    // 수집 기간 관리 패널
    const [periodPanelOpen, setPeriodPanelOpen] = useState(false);
    const [selectedPeriodKey, setSelectedPeriodKey] = useState<string>('');
    const [periodDropdownOpen, setPeriodDropdownOpen] = useState(false);
    const [periodEdits, setPeriodEdits] = useState<Record<string, PeriodEdit>>({});
    const [savingApiKey, setSavingApiKey] = useState<string | null>(null);
    const [executingApiKey, setExecutingApiKey] = useState<string | null>(null);
    const [manualToDate, setManualToDate] = useState<Record<string, boolean>>({});
    const [selectedDuration, setSelectedDuration] = useState<Record<string, number | null>>({});

    const getPeriodEdit = (p: CollectionPeriodDto): PeriodEdit => {
        if (periodEdits[p.apiKey]) return periodEdits[p.apiKey];
        return {
            fromDate: toDateInput(p.rangeFromDate),
            fromTime: toTimeInput(p.rangeFromDate),
            toDate: toDateInput(p.rangeToDate),
            toTime: toTimeInput(p.rangeToDate),
        };
    };

    const updatePeriodEdit = (apiKey: string, field: keyof PeriodEdit, value: string) => {
        const current = periodEdits[apiKey] || getPeriodEdit(periods.find((p) => p.apiKey === apiKey)!);
        setPeriodEdits((prev) => ({ ...prev, [apiKey]: { ...current, [field]: value } }));
    };

    const applyDurationPreset = (apiKey: string, hours: number) => {
        const p = periods.find((pp) => pp.apiKey === apiKey);
        if (!p) return;
        const edit = periodEdits[apiKey] || getPeriodEdit(p);
        if (!edit.fromDate) {
            showToast('재수집 시작일시를 먼저 선택해 주세요.', 'error');
            return;
        }
        const { toDate, toTime } = addHoursToDateTime(edit.fromDate, edit.fromTime, hours);
        setSelectedDuration((prev) => ({ ...prev, [apiKey]: hours }));
        setPeriodEdits((prev) => ({
            ...prev,
            [apiKey]: { ...edit, toDate, toTime },
        }));
    };

    const handleFromDateChange = (apiKey: string, field: 'fromDate' | 'fromTime', value: string) => {
        const p = periods.find((pp) => pp.apiKey === apiKey);
        if (!p) return;
        const edit = periodEdits[apiKey] || getPeriodEdit(p);
        const updated = { ...edit, [field]: value };
        // 기간 프리셋이 선택된 상태면 종료일시도 자동 갱신
        const dur = selectedDuration[apiKey];
        if (dur != null && !manualToDate[apiKey]) {
            const { toDate, toTime } = addHoursToDateTime(updated.fromDate, updated.fromTime, dur);
            updated.toDate = toDate;
            updated.toTime = toTime;
        }
        setPeriodEdits((prev) => ({ ...prev, [apiKey]: updated }));
    };

    const handleResetPeriod = async (p: CollectionPeriodDto) => {
        setSavingApiKey(p.apiKey);
        try {
            await batchApi.resetCollectionPeriod(p.apiKey);
            showToast(`${displayNameMap[p.apiKey] || p.apiKeyName || p.apiKey} 수집 기간이 초기화되었습니다.`, 'success');
            setPeriodEdits((prev) => { const next = { ...prev }; delete next[p.apiKey]; return next; });
            setSelectedDuration((prev) => ({ ...prev, [p.apiKey]: null }));
            setManualToDate((prev) => ({ ...prev, [p.apiKey]: false }));
            await loadPeriods();
        } catch (err) {
            showToast(err instanceof Error ? err.message : '수집 기간 초기화에 실패했습니다.', 'error');
        } finally {
            setSavingApiKey(null);
        }
    };

    const handleSavePeriod = async (p: CollectionPeriodDto) => {
        const edit = getPeriodEdit(p);
        if (!edit.fromDate || !edit.toDate) {
            showToast('시작일과 종료일을 모두 입력해 주세요.', 'error');
            return;
        }
        const from = toIsoDateTime(edit.fromDate, edit.fromTime);
        const to = toIsoDateTime(edit.toDate, edit.toTime);
        const now = new Date().toISOString().substring(0, 19);
        if (from >= now) {
            showToast('재수집 시작일시는 현재 시간보다 이전이어야 합니다.', 'error');
            return;
        }
        if (to >= now) {
            showToast('재수집 종료일시는 현재 시간보다 이전이어야 합니다.', 'error');
            return;
        }
        if (from >= to) {
            showToast('재수집 시작일시는 종료일시보다 이전이어야 합니다.', 'error');
            return;
        }
        setSavingApiKey(p.apiKey);
        try {
            await batchApi.updateCollectionPeriod(p.apiKey, { rangeFromDate: from, rangeToDate: to });
            showToast(`${displayNameMap[p.apiKey] || p.apiKeyName || p.apiKey} 수집 기간이 저장되었습니다.`, 'success');
            setPeriodEdits((prev) => { const next = { ...prev }; delete next[p.apiKey]; return next; });
            await loadPeriods();
        } catch (err) {
            showToast(err instanceof Error ? err.message : '수집 기간 저장에 실패했습니다.', 'error');
        } finally {
            setSavingApiKey(null);
        }
    };

    const handleExecuteRecollect = async (p: CollectionPeriodDto) => {
        if (!p.jobName) {
            showToast('연결된 Job이 없습니다.', 'error');
            return;
        }
        setExecutingApiKey(p.apiKey);
        try {
            const result = await batchApi.executeJob(p.jobName, {
                executionMode: 'RECOLLECT',
                apiKey: p.apiKey,
                executor: 'MANUAL',
                reason: '수집 기간 관리 화면에서 수동 실행',
            });
            showToast(result.message || `${displayNameMap[p.apiKey] || p.apiKeyName || p.apiKey} 재수집이 시작되었습니다.`, 'success');
            setLoading(true);
        } catch (err) {
            showToast(err instanceof Error ? err.message : '재수집 실행에 실패했습니다.', 'error');
        } finally {
            setExecutingApiKey(null);
        }
    };

    const loadLastCollectionStatuses = useCallback(async () => {
        try {
            const data = await batchApi.getLastCollectionStatuses();
            setLastCollectionStatuses(data);
        } catch {
            /* 수집 성공일시 로드 실패 무시 (폴링 중 반복 에러 toast 방지) */
        }
    }, []);

    const loadPeriods = useCallback(async () => {
        try {
            const data = await batchApi.getCollectionPeriods();
            setPeriods(data);
        } catch {
            /* 수집기간 로드 실패 무시 (폴링 중 반복 에러 toast 방지) */
        }
    }, []);

    const loadMetadata = useCallback(async () => {
        await Promise.all([loadLastCollectionStatuses(), loadPeriods()]);
    }, [loadLastCollectionStatuses, loadPeriods]);

    const [initialLoad, setInitialLoad] = useState(true);

    const loadHistories = useCallback(async () => {
        try {
            const params: {
                apiKey?: string;
                status?: string;
                fromDate?: string;
                toDate?: string;
                page?: number;
                size?: number;
            } = {
                page: useSearch ? page : 0,
                size: PAGE_SIZE,
            };
            if (selectedApiKey) params.apiKey = selectedApiKey;
            if (statusFilter !== 'ALL') params.status = statusFilter;
            if (useSearch && startDate) params.fromDate = `${startDate}T00:00:00`;
            if (useSearch && endDate) params.toDate = `${endDate}T23:59:59`;

            const data: RecollectionSearchResponse = await batchApi.searchRecollections(params);
            setHistories(data.content);
            setTotalPages(data.totalPages);
            setTotalCount(data.totalElements);
            setFailedRecordCounts(data.failedRecordCounts ?? {});
            if (!useSearch) setPage(data.number);
            setInitialLoad(false);
        } catch (err) {
            console.error('이력 조회 실패:', err);
            /* 초기 로드 실패만 toast, 폴링 중 실패는 console.error만 */
            if (initialLoad) {
                showToast('재수집 이력을 불러오지 못했습니다.', 'error');
            }
            setHistories([]);
            setTotalPages(0);
            setTotalCount(0);
        } finally {
            setLoading(false);
        }
    }, [selectedApiKey, statusFilter, startDate, endDate, useSearch, page, showToast, initialLoad]);

    usePoller(loadMetadata, 60_000, []);
    usePoller(loadHistories, POLLING_INTERVAL_MS, [selectedApiKey, statusFilter, useSearch, page]);

    const collectionStatusSummary = useMemo(() => {
        let normal = 0, warn = 0, danger = 0;
        for (const s of lastCollectionStatuses) {
            const level = getStatusLevel(s.elapsedMinutes);
            if (level === 'normal') normal++;
            else if (level === 'warn') warn++;
            else danger++;
        }
        return { normal, warn, danger, total: lastCollectionStatuses.length };
    }, [lastCollectionStatuses]);

    const filteredHistories = useMemo(() => {
        if (useSearch) return histories;
        if (statusFilter === 'ALL') return histories;
        return histories.filter((h) => h.executionStatus === statusFilter);
    }, [histories, statusFilter, useSearch]);

    const handleSearch = async () => {
        setUseSearch(true);
        setPage(0);
        setLoading(true);
        await loadHistories();
    };

    const handleResetSearch = () => {
        setUseSearch(false);
        setStartDate('');
        setEndDate('');
        setPage(0);
        setTotalPages(0);
        setTotalCount(0);
        setLoading(true);
    };

    const handlePageChange = (newPage: number) => {
        if (newPage < 0 || newPage >= totalPages) return;
        setPage(newPage);
        setLoading(true);
    };

    useEffect(() => {
        batchApi.getDisplayNames()
            .then(setDisplayNames)
            .catch(() => { /* displayName 로드 실패 무시 */ });
    }, []);

    const displayNameMap = useMemo<Record<string, string>>(() => {
        const map: Record<string, string> = {};
        for (const dn of displayNames) {
            if (dn.apiKey) map[dn.apiKey] = dn.displayName;
        }
        return map;
    }, [displayNames]);

    const getApiLabel = (apiKey: string) => {
        if (displayNameMap[apiKey]) return displayNameMap[apiKey];
        const p = periods.find((p) => p.apiKey === apiKey);
        return p?.apiKeyName || apiKey;
    };

    return (
        <div className="space-y-6">
            {/* 헤더 */}
            <div>
                <div className="flex items-center gap-2">
                    <h1 className="text-2xl font-bold text-wing-text">재수집 이력</h1>
                    <HelpButton onClick={() => setGuideOpen(true)} />
                </div>
                <p className="mt-1 text-sm text-wing-muted">
                    배치 재수집 실행 이력을 조회하고 관리합니다.
                </p>
            </div>

            {/* 마지막 수집 성공일시 패널 */}
            <div className="bg-wing-surface rounded-xl shadow-md">
                <button
                    onClick={() => setLastCollectionPanelOpen((v) => !v)}
                    className="w-full flex items-center justify-between px-6 py-4 hover:bg-wing-hover transition-colors rounded-xl"
                >
                    <div className="flex items-center gap-2">
                        <svg
                            className={`w-4 h-4 text-wing-muted transition-transform ${lastCollectionPanelOpen ? 'rotate-90' : ''}`}
                            fill="none" viewBox="0 0 24 24" stroke="currentColor"
                        >
                            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
                        </svg>
                        <span className="text-sm font-semibold text-wing-text">마지막 수집 완료일시</span>
                        <span className="text-xs text-wing-muted">({collectionStatusSummary.total}건)</span>
                        {collectionStatusSummary.danger > 0 && (
                            <span className="ml-1 inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-red-500/10 text-red-600 dark:text-red-400">
                                경고 {collectionStatusSummary.danger}
                            </span>
                        )}
                        {collectionStatusSummary.warn > 0 && (
                            <span className="ml-1 inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-yellow-500/10 text-yellow-600 dark:text-yellow-400">
                                주의 {collectionStatusSummary.warn}
                            </span>
                        )}
                    </div>
                    <span className="text-xs text-wing-muted">
                        {lastCollectionPanelOpen ? '접기' : '펼치기'}
                    </span>
                </button>

                {lastCollectionPanelOpen && (
                    <div className="border-t border-wing-border/50 px-6 py-4 space-y-4">
                        {lastCollectionStatuses.length === 0 ? (
                            <div className="py-4 text-center text-sm text-wing-muted">
                                수집 이력이 없습니다.
                            </div>
                        ) : (
                            <>
                                {/* 요약 바 */}
                                <div className="flex items-center gap-4 text-sm">
                                    <div className="flex items-center gap-1.5">
                                        <span className="inline-block w-2.5 h-2.5 rounded-full bg-green-500" />
                                        <span className="text-wing-muted">정상</span>
                                        <span className="font-semibold text-wing-text">{collectionStatusSummary.normal}</span>
                                    </div>
                                    <div className="flex items-center gap-1.5">
                                        <span className="inline-block w-2.5 h-2.5 rounded-full bg-yellow-500" />
                                        <span className="text-wing-muted">주의</span>
                                        <span className="font-semibold text-wing-text">{collectionStatusSummary.warn}</span>
                                    </div>
                                    <div className="flex items-center gap-1.5">
                                        <span className="inline-block w-2.5 h-2.5 rounded-full bg-red-500" />
                                        <span className="text-wing-muted">경고</span>
                                        <span className="font-semibold text-wing-text">{collectionStatusSummary.danger}</span>
                                    </div>
                                    <span className="text-xs text-wing-muted ml-auto">
                                        기준: 정상 24h 이내 · 주의 24~48h · 경고 48h 초과
                                    </span>
                                </div>

                                {/* 테이블 */}
                                <div className="overflow-x-auto">
                                    <table className="w-full text-sm">
                                        <thead>
                                            <tr className="border-b border-wing-border/50">
                                                <th className="text-left py-2 px-3 text-xs font-medium text-wing-muted">배치 작업명</th>
                                                <th className="text-left py-2 px-3 text-xs font-medium text-wing-muted">마지막 수집 완료일시</th>
                                                <th className="text-left py-2 px-3 text-xs font-medium text-wing-muted">경과시간</th>
                                                <th className="text-center py-2 px-3 text-xs font-medium text-wing-muted">상태</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            {lastCollectionStatuses.map((s) => {
                                                const level = getStatusLevel(s.elapsedMinutes);
                                                return (
                                                    <tr key={s.apiKey} className="border-b border-wing-border/30 hover:bg-wing-hover/50 transition-colors">
                                                        <td className="py-2.5 px-3">
                                                            <div className="text-wing-text font-medium">{displayNameMap[s.apiKey] || s.apiDesc || s.apiKey}</div>
                                                        </td>
                                                        <td className="py-2.5 px-3 font-mono text-xs text-wing-muted">
                                                            {formatDateTime(s.lastSuccessDate)}
                                                        </td>
                                                        <td className={`py-2.5 px-3 text-xs font-medium ${getStatusColor(level)}`}>
                                                            {formatElapsed(s.elapsedMinutes)}
                                                        </td>
                                                        <td className="py-2.5 px-3 text-center">
                                                            <span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium ${getStatusBgColor(level)}`}>
                                                                {level === 'normal' && '●'}
                                                                {level === 'warn' && '▲'}
                                                                {level === 'danger' && '■'}
                                                                {' '}{getStatusLabel(level)}
                                                            </span>
                                                        </td>
                                                    </tr>
                                                );
                                            })}
                                        </tbody>
                                    </table>
                                </div>
                            </>
                        )}
                    </div>
                )}
            </div>

            {/* 수집 기간 관리 패널 */}
            <div className="bg-wing-surface rounded-xl shadow-md">
                <button
                    onClick={() => setPeriodPanelOpen((v) => !v)}
                    className="w-full flex items-center justify-between px-6 py-4 hover:bg-wing-hover transition-colors rounded-xl"
                >
                    <div className="flex items-center gap-2">
                        <svg
                            className={`w-4 h-4 text-wing-muted transition-transform ${periodPanelOpen ? 'rotate-90' : ''}`}
                            fill="none" viewBox="0 0 24 24" stroke="currentColor"
                        >
                            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
                        </svg>
                        <span className="text-sm font-semibold text-wing-text">재수집 기간 관리</span>
                        <span className="text-xs text-wing-muted">({periods.length}건)</span>
                    </div>
                    <span className="text-xs text-wing-muted">
                        {periodPanelOpen ? '접기' : '펼치기'}
                    </span>
                </button>

                {periodPanelOpen && (
                    <div className="border-t border-wing-border/50 px-6 py-4 space-y-4">
                        {periods.length === 0 ? (
                            <div className="py-4 text-center text-sm text-wing-muted">
                                등록된 수집 기간이 없습니다.
                            </div>
                        ) : (
                            <>
                                {/* 작업 선택 드롭다운 */}
                                <div className="flex items-center gap-3">
                                    <label className="text-sm font-medium text-wing-text shrink-0">
                                        작업 선택
                                    </label>
                                    <div className="relative">
                                        <button
                                            onClick={() => setPeriodDropdownOpen((v) => !v)}
                                            className="inline-flex items-center gap-1.5 px-3 py-2 text-sm rounded-lg border border-wing-border bg-wing-surface hover:bg-wing-hover transition-colors min-w-[200px] justify-between"
                                        >
                                            <span className={selectedPeriodKey ? 'text-wing-text' : 'text-wing-muted'}>
                                                {selectedPeriodKey
                                                    ? (displayNameMap[selectedPeriodKey] || periods.find((p) => p.apiKey === selectedPeriodKey)?.apiKeyName || selectedPeriodKey)
                                                    : '작업을 선택하세요'}
                                            </span>
                                            <svg className={`w-4 h-4 text-wing-muted transition-transform ${periodDropdownOpen ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
                                            </svg>
                                        </button>
                                        {periodDropdownOpen && (
                                            <>
                                                <div className="fixed inset-0 z-10" onClick={() => setPeriodDropdownOpen(false)} />
                                                <div className="absolute z-20 mt-1 w-72 max-h-60 overflow-y-auto bg-wing-surface border border-wing-border rounded-lg shadow-lg">
                                                    {periods.map((p) => (
                                                        <button
                                                            key={p.apiKey}
                                                            onClick={() => {
                                                                setSelectedPeriodKey(p.apiKey);
                                                                setPeriodDropdownOpen(false);
                                                            }}
                                                            className={`w-full text-left px-3 py-2 text-sm hover:bg-wing-hover transition-colors ${
                                                                selectedPeriodKey === p.apiKey
                                                                    ? 'bg-wing-accent/10 text-wing-accent font-medium'
                                                                    : 'text-wing-text'
                                                            }`}
                                                        >
                                                            <div>{displayNameMap[p.apiKey] || p.apiKeyName || p.apiKey}</div>
                                                        </button>
                                                    ))}
                                                </div>
                                            </>
                                        )}
                                    </div>
                                </div>

                                {/* 선택된 작업의 기간 편집 */}
                                {selectedPeriodKey && (() => {
                                    const p = periods.find((pp) => pp.apiKey === selectedPeriodKey);
                                    if (!p) return null;
                                    const edit = getPeriodEdit(p);
                                    const hasChange = !!periodEdits[p.apiKey];
                                    const isSaving = savingApiKey === p.apiKey;
                                    const isExecuting = executingApiKey === p.apiKey;
                                    const isManual = !!manualToDate[p.apiKey];
                                    const activeDur = selectedDuration[p.apiKey] ?? null;
                                    return (
                                        <div className="bg-wing-card rounded-lg p-4 space-y-3">
                                            <div className="flex items-center gap-4 text-sm">
                                                <span className="text-wing-muted">작업명:</span>
                                                <span className="font-mono text-xs text-wing-text">{p.jobName || '-'}</span>
                                            </div>

                                            {/* Line 1: 재수집 시작일시 */}
                                            <div>
                                                <label className="block text-xs font-medium text-wing-muted mb-1">재수집 시작일시</label>
                                                <div className="flex items-center gap-1">
                                                    <input
                                                        type="date"
                                                        value={edit.fromDate}
                                                        onChange={(e) => handleFromDateChange(p.apiKey, 'fromDate', e.target.value)}
                                                        className="flex-[3] min-w-0 rounded border border-wing-border bg-wing-surface px-2 py-1.5 text-sm shadow-sm focus:border-wing-accent focus:ring-1 focus:ring-wing-accent"
                                                    />
                                                    <input
                                                        type="time"
                                                        value={edit.fromTime}
                                                        onChange={(e) => handleFromDateChange(p.apiKey, 'fromTime', e.target.value)}
                                                        className="flex-[2] min-w-0 rounded border border-wing-border bg-wing-surface px-2 py-1.5 text-sm shadow-sm focus:border-wing-accent focus:ring-1 focus:ring-wing-accent"
                                                    />
                                                </div>
                                            </div>

                                            {/* Line 2: 기간 선택 버튼 + 직접입력 토글 */}
                                            <div className="flex items-center gap-2 flex-wrap">
                                                {DURATION_PRESETS.map(({ label, hours }) => (
                                                    <button
                                                        key={hours}
                                                        onClick={() => {
                                                            setManualToDate((prev) => ({ ...prev, [p.apiKey]: false }));
                                                            applyDurationPreset(p.apiKey, hours);
                                                        }}
                                                        className={`px-3 py-1.5 text-xs font-medium rounded-lg border transition-colors ${
                                                            !isManual && activeDur === hours
                                                                ? 'bg-wing-accent text-white border-wing-accent shadow-sm'
                                                                : 'bg-wing-surface text-wing-muted border-wing-border hover:bg-wing-hover'
                                                        }`}
                                                    >
                                                        {label}
                                                    </button>
                                                ))}
                                                <div className="ml-auto flex items-center gap-1.5">
                                                    <span className="text-xs text-wing-muted">직접입력</span>
                                                    <button
                                                        onClick={() => {
                                                            const next = !isManual;
                                                            setManualToDate((prev) => ({ ...prev, [p.apiKey]: next }));
                                                            if (next) {
                                                                setSelectedDuration((prev) => ({ ...prev, [p.apiKey]: null }));
                                                            }
                                                        }}
                                                        className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${
                                                            isManual ? 'bg-wing-accent' : 'bg-wing-border'
                                                        }`}
                                                    >
                                                        <span
                                                            className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform ${
                                                                isManual ? 'translate-x-4' : 'translate-x-0'
                                                            }`}
                                                        />
                                                    </button>
                                                </div>
                                            </div>

                                            {/* Line 3: 재수집 종료일시 */}
                                            <div>
                                                <label className="block text-xs font-medium text-wing-muted mb-1">재수집 종료일시</label>
                                                <div className="flex items-center gap-1">
                                                    <input
                                                        type="date"
                                                        value={edit.toDate}
                                                        disabled={!isManual}
                                                        onChange={(e) => updatePeriodEdit(p.apiKey, 'toDate', e.target.value)}
                                                        className={`flex-[3] min-w-0 rounded border border-wing-border px-2 py-1.5 text-sm shadow-sm focus:border-wing-accent focus:ring-1 focus:ring-wing-accent ${
                                                            isManual ? 'bg-wing-surface' : 'bg-wing-card text-wing-muted cursor-not-allowed'
                                                        }`}
                                                    />
                                                    <input
                                                        type="time"
                                                        value={edit.toTime}
                                                        disabled={!isManual}
                                                        onChange={(e) => updatePeriodEdit(p.apiKey, 'toTime', e.target.value)}
                                                        className={`flex-[2] min-w-0 rounded border border-wing-border px-2 py-1.5 text-sm shadow-sm focus:border-wing-accent focus:ring-1 focus:ring-wing-accent ${
                                                            isManual ? 'bg-wing-surface' : 'bg-wing-card text-wing-muted cursor-not-allowed'
                                                        }`}
                                                    />
                                                </div>
                                            </div>

                                            <div className="flex items-center justify-end gap-2 pt-1">
                                                <button
                                                    onClick={() => handleResetPeriod(p)}
                                                    disabled={isSaving}
                                                    className="px-4 py-2 text-sm font-medium rounded-lg text-red-600 bg-red-50 hover:bg-red-100 border border-red-200 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
                                                >
                                                    기간 초기화
                                                </button>
                                                <button
                                                    onClick={() => handleSavePeriod(p)}
                                                    disabled={isSaving || !hasChange}
                                                    className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
                                                        hasChange
                                                            ? 'text-white bg-wing-accent hover:bg-wing-accent/80 shadow-sm'
                                                            : 'text-wing-muted bg-wing-surface border border-wing-border cursor-not-allowed'
                                                    }`}
                                                >
                                                    {isSaving ? '저장중...' : '기간 저장'}
                                                </button>
                                                <button
                                                    onClick={() => handleExecuteRecollect(p)}
                                                    disabled={isExecuting || !p.jobName}
                                                    className="px-4 py-2 text-sm font-medium rounded-lg text-emerald-700 bg-emerald-50 hover:bg-emerald-100 border border-emerald-200 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
                                                >
                                                    {isExecuting ? '실행중...' : '재수집 실행'}
                                                </button>
                                            </div>
                                        </div>
                                    );
                                })()}
                            </>
                        )}
                    </div>
                )}
            </div>

            {/* 필터 영역 */}
            <div className="bg-wing-surface rounded-xl shadow-md p-6">
                <div className="space-y-3">
                    {/* API 선택 */}
                    <div>
                        <div className="flex items-center gap-3 mb-2">
                            <label className="text-sm font-medium text-wing-text shrink-0">
                                재수집 작업 선택
                            </label>
                            <div className="relative">
                                <button
                                    onClick={() => setApiDropdownOpen((v) => !v)}
                                    className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-wing-border bg-wing-surface hover:bg-wing-hover transition-colors"
                                >
                                    {selectedApiKey
                                        ? getApiLabel(selectedApiKey)
                                        : '전체'}
                                    <svg className={`w-4 h-4 text-wing-muted transition-transform ${apiDropdownOpen ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
                                </button>
                                {apiDropdownOpen && (
                                    <>
                                        <div className="fixed inset-0 z-10" onClick={() => setApiDropdownOpen(false)} />
                                        <div className="absolute z-20 mt-1 w-72 max-h-60 overflow-y-auto bg-wing-surface border border-wing-border rounded-lg shadow-lg">
                                            <button
                                                onClick={() => { setSelectedApiKey(''); setApiDropdownOpen(false); setPage(0); setLoading(true); }}
                                                className={`w-full text-left px-3 py-2 text-sm hover:bg-wing-hover transition-colors ${
                                                    !selectedApiKey ? 'bg-wing-accent/10 text-wing-accent font-medium' : 'text-wing-text'
                                                }`}
                                            >
                                                전체
                                            </button>
                                            {periods.map((p) => (
                                                <button
                                                    key={p.apiKey}
                                                    onClick={() => { setSelectedApiKey(p.apiKey); setApiDropdownOpen(false); setPage(0); setLoading(true); }}
                                                    className={`w-full text-left px-3 py-2 text-sm hover:bg-wing-hover transition-colors ${
                                                        selectedApiKey === p.apiKey ? 'bg-wing-accent/10 text-wing-accent font-medium' : 'text-wing-text'
                                                    }`}
                                                >
                                                    {displayNameMap[p.apiKey] || p.apiKeyName || p.apiKey}
                                                </button>
                                            ))}
                                        </div>
                                    </>
                                )}
                            </div>
                            {selectedApiKey && (
                                <button
                                    onClick={() => { setSelectedApiKey(''); setPage(0); setLoading(true); }}
                                    className="text-xs text-wing-muted hover:text-wing-accent transition-colors"
                                >
                                    초기화
                                </button>
                            )}
                        </div>
                        {selectedApiKey && (
                            <div className="flex flex-wrap gap-1.5">
                                <span className="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-wing-accent/15 text-wing-accent rounded-full">
                                    {getApiLabel(selectedApiKey)}
                                    <button
                                        onClick={() => { setSelectedApiKey(''); setPage(0); setLoading(true); }}
                                        className="hover:text-wing-text transition-colors"
                                    >
                                        &times;
                                    </button>
                                </span>
                            </div>
                        )}
                    </div>

                    {/* 상태 필터 버튼 그룹 */}
                    <div className="flex flex-wrap gap-1">
                        {STATUS_FILTERS.map(({ value, label }) => (
                            <button
                                key={value}
                                onClick={() => {
                                    setStatusFilter(value);
                                    setPage(0);
                                    setLoading(true);
                                }}
                                className={`px-3 py-1.5 text-xs font-medium rounded-lg transition-colors ${
                                    statusFilter === value
                                        ? 'bg-wing-accent text-white shadow-sm'
                                        : 'bg-wing-card text-wing-muted hover:bg-wing-hover'
                                }`}
                            >
                                {label}
                            </button>
                        ))}
                    </div>
                </div>

                {/* 날짜 범위 필터 */}
                <div className="flex flex-col gap-3 sm:flex-row sm:items-center mt-4 pt-4 border-t border-wing-border/50">
                    <label className="text-sm font-medium text-wing-text shrink-0">
                        기간 검색
                    </label>
                    <div className="flex items-center gap-2">
                        <input
                            type="date"
                            value={startDate}
                            onChange={(e) => setStartDate(e.target.value)}
                            className="block rounded-lg border border-wing-border bg-wing-surface px-3 py-2 text-sm shadow-sm focus:border-wing-accent focus:ring-1 focus:ring-wing-accent"
                        />
                        <span className="text-wing-muted text-sm">~</span>
                        <input
                            type="date"
                            value={endDate}
                            onChange={(e) => setEndDate(e.target.value)}
                            className="block rounded-lg border border-wing-border bg-wing-surface px-3 py-2 text-sm shadow-sm focus:border-wing-accent focus:ring-1 focus:ring-wing-accent"
                        />
                    </div>
                    <div className="flex items-center gap-2">
                        <button
                            onClick={handleSearch}
                            className="px-4 py-2 text-sm font-medium text-white bg-wing-accent rounded-lg hover:bg-wing-accent/80 transition-colors shadow-sm"
                        >
                            검색
                        </button>
                        {useSearch && (
                            <button
                                onClick={handleResetSearch}
                                className="px-4 py-2 text-sm font-medium text-wing-text bg-wing-card rounded-lg hover:bg-wing-hover transition-colors"
                            >
                                초기화
                            </button>
                        )}
                        <button
                            onClick={() => batchApi.exportRecollectionHistories({
                                apiKey: selectedApiKey || undefined,
                                status: statusFilter !== 'ALL' ? statusFilter : undefined,
                                fromDate: useSearch && startDate ? `${startDate}T00:00:00` : undefined,
                                toDate: useSearch && endDate ? `${endDate}T23:59:59` : undefined,
                            })}
                            className="px-4 py-2 text-sm font-medium text-wing-text border border-wing-border rounded-lg hover:bg-wing-hover transition-colors"
                        >
                            CSV 내보내기
                        </button>
                    </div>
                </div>
            </div>

            {/* 재수집 이력 테이블 */}
            <div className="bg-wing-surface rounded-xl shadow-md overflow-hidden">
                {loading ? (
                    <LoadingSpinner />
                ) : filteredHistories.length === 0 ? (
                    <EmptyState
                        message="재수집 이력이 없습니다."
                        sub={
                            statusFilter !== 'ALL'
                                ? '다른 상태 필터를 선택해 보세요.'
                                : selectedApiKey
                                  ? '선택한 API의 재수집 이력이 없습니다.'
                                  : undefined
                        }
                    />
                ) : (
                    <div className="overflow-x-auto">
                        <table className="w-full text-sm text-left">
                            <thead className="bg-wing-card text-xs uppercase text-wing-muted">
                                <tr>
                                    <th className="px-4 py-3 font-medium">재수집 ID</th>
                                    <th className="px-4 py-3 font-medium">작업명</th>
                                    <th className="px-4 py-3 font-medium">상태</th>
                                    <th className="px-4 py-3 font-medium">재수집 시작일시</th>
                                    <th className="px-4 py-3 font-medium">재수집 종료일시</th>
                                    <th className="px-4 py-3 font-medium">소요시간</th>
                                    <th className="px-4 py-3 font-medium text-center">실패건</th>
                                    <th className="px-4 py-3 font-medium text-right">액션</th>
                                </tr>
                            </thead>
                            <tbody className="divide-y divide-wing-border/50">
                                {filteredHistories.map((hist) => (
                                    <tr
                                        key={hist.historyId}
                                        className="hover:bg-wing-hover transition-colors"
                                    >
                                        <td className="px-4 py-4 font-mono text-wing-text">
                                            #{hist.historyId}
                                        </td>
                                        <td className="px-4 py-4 text-wing-text">
                                            <div className="max-w-[120px] truncate" title={displayNameMap[hist.apiKey] || hist.apiKeyName || hist.apiKey}>
                                                {displayNameMap[hist.apiKey] || hist.apiKeyName || hist.apiKey}
                                            </div>
                                        </td>
                                      <td className="px-4 py-4">
                                          {hist.executionStatus === 'FAILED' ? (
                                                <button
                                                    onClick={() => setFailLogTarget(hist)}
                                                    className="cursor-pointer"
                                                    title="클릭하여 실패 로그 확인"
                                                >
                                                    <StatusBadge status={hist.executionStatus} />
                                                </button>
                                            ) : (
                                                <StatusBadge status={hist.executionStatus} />
                                            )}
                                            {hist.hasOverlap && (
                                                <span className="ml-1 text-xs text-amber-500" title="기간 중복 감지">
                                                    !</span>
                                            )}
                                        </td>
                                      <td className="px-4 py-4 text-wing-muted whitespace-nowrap text-xs">
                                        <div>{formatDateTime(hist.rangeFromDate)}</div>
                                      </td>
                                      <td className="px-4 py-4 text-wing-muted whitespace-nowrap text-xs">
                                        <div>{formatDateTime(hist.rangeToDate)}</div>
                                      </td>
                                        <td className="px-4 py-4 text-wing-muted whitespace-nowrap">
                                            {formatDuration(hist.durationMs)}
                                        </td>
                                        <td className="px-4 py-4 text-center">
                                            {(() => {
                                                const count = hist.jobExecutionId
                                                    ? (failedRecordCounts[hist.jobExecutionId] ?? 0)
                                                    : 0;
                                                if (hist.executionStatus === 'STARTED') {
                                                    return <span className="text-xs text-wing-muted">-</span>;
                                                }
                                                return count > 0 ? (
                                                    <span className="inline-flex items-center px-2 py-0.5 text-xs font-semibold rounded-full bg-red-100 text-red-700">
                                                        {count}건
                                                    </span>
                                                ) : (
                                                    <span className="text-xs text-wing-muted">0</span>
                                                );
                                            })()}
                                        </td>
                                        <td className="px-4 py-4 text-right">
                                            <button
                                                onClick={() => navigate(`/recollects/${hist.historyId}`)}
                                                className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-wing-accent bg-wing-accent/10 rounded-lg hover:bg-wing-accent/15 transition-colors"
                                            >
                                                상세
                                            </button>
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}

                {/* 결과 건수 + 페이지네이션 */}
                {!loading && filteredHistories.length > 0 && (
                    <div className="px-6 py-3 bg-wing-card border-t border-wing-border/50 flex items-center justify-between">
                        <div className="text-xs text-wing-muted">
                            총 {totalCount}건
                        </div>
                        {totalPages > 1 && (
                            <div className="flex items-center gap-2">
                                <button
                                    onClick={() => handlePageChange(page - 1)}
                                    disabled={page === 0}
                                    className="px-3 py-1.5 text-xs font-medium rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed bg-wing-card text-wing-muted hover:bg-wing-hover"
                                >
                                    이전
                                </button>
                                <span className="text-xs text-wing-muted">
                                    {page + 1} / {totalPages}
                                </span>
                                <button
                                    onClick={() => handlePageChange(page + 1)}
                                    disabled={page >= totalPages - 1}
                                    className="px-3 py-1.5 text-xs font-medium rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed bg-wing-card text-wing-muted hover:bg-wing-hover"
                                >
                                    다음
                                </button>
                            </div>
                        )}
                    </div>
                )}
            </div>

            {/* 실패 로그 뷰어 모달 */}
            <InfoModal
                open={failLogTarget !== null}
                title={
                    failLogTarget
                        ? `실패 로그 - #${failLogTarget.historyId} (${failLogTarget.jobName})`
                        : '실패 로그'
                }
                onClose={() => setFailLogTarget(null)}
            >
                {failLogTarget && (
                    <div className="space-y-4">
                        <div>
                            <h4 className="text-xs font-semibold text-wing-muted uppercase mb-1">
                                실행 상태
                            </h4>
                            <p className="text-sm text-wing-text font-mono bg-wing-card px-3 py-2 rounded-lg">
                                {failLogTarget.executionStatus}
                            </p>
                        </div>
                        <div>
                            <h4 className="text-xs font-semibold text-wing-muted uppercase mb-1">
                                실패 사유
                            </h4>
                            <pre className="text-sm text-wing-text font-mono bg-wing-card px-3 py-2 rounded-lg whitespace-pre-wrap break-words max-h-64 overflow-y-auto">
                                {failLogTarget.failureReason || '실패 사유 없음'}
                            </pre>
                        </div>
                    </div>
                )}
            </InfoModal>

            <GuideModal
                open={guideOpen}
                pageTitle="재수집 이력"
                sections={RECOLLECTS_GUIDE}
                onClose={() => setGuideOpen(false)}
            />
        </div>
    );
}
