{% extends "base.html" %}

{% block title %}전표비교{% endblock %}

{% block head_extra %}
<style>
    .voucher-page {
        display: grid;
        gap: 14px;
    }

    .voucher-topbar {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 12px;
        flex-wrap: wrap;
    }

    .voucher-title-wrap h2 {
        margin: 0;
    }

    .voucher-toolbar {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-wrap: nowrap;
        overflow-x: auto;
    }

    .voucher-toolbar form,
    .filter-form {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-wrap: nowrap;
        margin: 0;
        overflow: visible;
    }

    .voucher-toolbar label,
    .filter-form label {
        font-size: 13px;
        color: var(--muted);
        white-space: nowrap;
    }

    .voucher-toolbar select,
    .voucher-toolbar button,
    .filter-form input,
    .filter-form button,
    .upload-picker {
        height: 38px;
        box-sizing: border-box;
        white-space: nowrap;
    }

    .voucher-toolbar select,
    .filter-form input {
        min-width: 104px;
        padding: 0 30px 0 10px;
        font-size: 14px;
        line-height: 38px;
    }

    .filter-form input {
        min-width: 120px;
        padding-right: 10px;
    }

    .detail-head .filter-form {
        display: flex;
        flex-wrap: nowrap;
        align-items: center;
        gap: 8px;
        width: 100%;
        max-width: 100%;
        min-width: 0;
        overflow: visible;
        row-gap: 0;
    }

    .detail-head .filter-form input {
        min-width: 98px;
    }

    .detail-panel[data-status-panel="amount_mismatch"] .detail-head {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        align-items: start;
        row-gap: 6px;
        overflow: visible;
    }

    .detail-panel[data-status-panel="amount_mismatch"] .detail-head > strong {
        grid-column: 1 / -1;
        grid-row: 1;
        white-space: nowrap;
    }

    .detail-panel[data-status-panel="amount_mismatch"] .detail-head > .filter-form {
        grid-column: 1 / -1;
        grid-row: 2;
        width: 100%;
        max-width: 100%;
        min-width: 0;
        overflow: visible;
        padding-bottom: 2px;
    }

    .detail-panel[data-status-panel="amount_mismatch"] .detail-head > .result-meta {
        grid-column: 1 / -1;
        grid-row: 3;
    }

    .active-filter-cards {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
        gap: 8px;
        margin-top: 8px;
    }

    .active-filter-card {
        min-width: 0;
        border: 1px solid #d3dee8;
        border-radius: 10px;
        padding: 6px 8px;
        background: #f8fafc;
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        gap: 6px;
        align-items: center;
    }

    .active-filter-card .value {
        min-width: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        font-size: 12px;
        color: #0f172a;
        font-weight: 700;
    }

    .active-filter-card .remove {
        width: 18px;
        height: 18px;
        border-radius: 999px;
        border: 0;
        background: rgba(15, 23, 42, 0.08);
        color: #111827;
        box-shadow: none;
        transform: none;
        padding: 0;
        cursor: pointer;
        line-height: 1;
        font-size: 13px;
        font-weight: 800;
    }

    .active-filter-card .remove:hover {
        background: rgba(15, 23, 42, 0.14);
    }

    .panel-shell {
        border: 1px solid var(--line);
        border-radius: 16px;
        background: #fff;
        overflow: hidden;
        box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
    }

    .panel-header {
        padding: 12px 16px 8px;
        display: flex;
        justify-content: space-between;
        gap: 10px;
        align-items: center;
        flex-wrap: wrap;
    }

    .panel-header h2,
    .summary-copy h2 {
        margin: 0;
        font-size: 18px;
    }

    .panel-meta,
    .empty-note,
    .result-meta {
        color: var(--muted);
        line-height: 1.5;
        font-size: 13px;
    }

    .panel-body {
        padding: 0 16px 16px;
        min-width: 0;
    }

    .status-grid {
        display: flex;
        gap: 10px;
        flex-wrap: wrap;
    }

    .status-card {
        min-width: 130px;
        width: auto;
        border: 1px solid var(--line);
        border-radius: 14px;
        background: #fff;
        padding: 10px 12px;
        text-align: left;
        cursor: pointer;
        transition: background-color 0.16s ease, color 0.16s ease, border-color 0.16s ease;
    }

    .status-card:hover,
    .status-card[aria-expanded="true"] {
        background: #111827;
        border-color: #111827;
        color: #fff;
    }

    .status-label {
        display: block;
        font-size: 12px;
        font-weight: 700;
        color: var(--muted);
        margin-bottom: 4px;
    }

    .status-card:hover .status-label,
    .status-card[aria-expanded="true"] .status-label {
        color: rgba(255, 255, 255, 0.82);
    }

    .status-value {
        display: block;
        font-size: 26px;
        font-weight: 800;
        line-height: 1;
        color: #111;
    }

    .status-card:hover .status-value,
    .status-card[aria-expanded="true"] .status-value {
        color: #fff;
    }

    .detail-stack {
        display: grid;
        gap: 10px;
        margin-top: 10px;
        min-width: 0;
    }

    .detail-panel {
        display: none;
        border: 1px solid var(--line);
        border-radius: 14px;
        background: #fff;
        overflow: visible;
        min-width: 0;
    }

    .detail-panel.active {
        display: block;
    }

    .detail-head {
        padding: 10px 12px;
        border-bottom: 1px solid var(--line);
        background: #f8fafc;
        display: grid;
        gap: 10px;
        position: relative;
        z-index: 6;
        overflow: visible;
    }

    .data-panel summary {
        list-style: none;
        cursor: pointer;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 12px;
        padding: 12px 16px;
    }

    .data-panel summary::-webkit-details-marker {
        display: none;
    }

    .summary-badges {
        display: flex;
        gap: 8px;
        align-items: center;
        flex-wrap: wrap;
    }

    .summary-badge {
        display: inline-flex;
        align-items: center;
        border-radius: 999px;
        padding: 5px 9px;
        background: #f3f4f6;
        color: #111827;
        font-size: 12px;
        font-weight: 700;
        white-space: nowrap;
    }

    .summary-toggle {
        font-size: 12px;
        color: var(--muted);
    }

    .data-panel[open] .summary-toggle::after {
        content: "접기";
    }

    .data-panel:not([open]) .summary-toggle::after {
        content: "펼치기";
    }

    .table-wrap {
        width: 100%;
        max-width: 100%;
        overflow: auto;
        border-top: 1px solid var(--line);
        background: #fff;
        max-height: 460px;
        box-sizing: border-box;
    }

    .detail-panel .table-wrap {
        max-height: 360px;
        overflow-x: auto;
        overflow-y: auto;
    }

    .table-footer {
        display: flex;
        justify-content: center;
        padding: 10px 12px 14px;
        border-top: 1px solid var(--line);
        background: #fff;
    }

    .load-more-button {
        min-width: 120px;
    }

    .table-wrap table {
        width: max-content;
        min-width: max(100%, 1040px);
        border-collapse: collapse;
        table-layout: fixed;
    }

    .table-wrap th,
    .table-wrap td {
        border-bottom: 1px solid rgba(148, 163, 184, 0.18);
        padding: 7px 8px;
        vertical-align: middle;
        text-align: left;
        font-size: 13px;
        line-height: 1.35;
    }

    .table-wrap th {
        position: sticky;
        top: 0;
        z-index: 1;
        background: #f8fafc;
        font-size: 13px;
        letter-spacing: 0.01em;
    }

    .cell-ellipsis {
        max-width: 180px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        display: block;
    }

    .cell-tight {
        max-width: 86px;
    }

    .cell-code {
        max-width: 108px;
    }

    .cell-equal {
        max-width: 128px;
    }

    .cell-desc {
        max-width: 220px;
    }

    .mono {
        font-family: "Consolas", "Courier New", monospace;
        font-size: 13px;
    }

    .upload-form {
        display: flex;
        gap: 8px;
        align-items: center;
        flex-wrap: nowrap;
        overflow-x: auto;
        padding-bottom: 12px;
    }

    .upload-picker {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        padding: 0 12px;
        border-radius: 10px;
        border: 1px solid var(--line);
        background: #fff;
        cursor: pointer;
        font-weight: 700;
        transition: border-color 0.16s ease, box-shadow 0.16s ease;
    }

    .upload-picker:hover {
        border-color: #9ca3af;
        box-shadow: 0 8px 18px rgba(15, 23, 42, 0.08);
    }

    .upload-picker::before {
        content: "";
        width: 15px;
        height: 11px;
        border: 2px solid currentColor;
        border-top-left-radius: 3px;
        border-top-right-radius: 3px;
        border-bottom-left-radius: 2px;
        border-bottom-right-radius: 2px;
        box-sizing: border-box;
        position: relative;
        display: inline-block;
        transform: translateY(1px);
        clip-path: polygon(0 28%, 22% 28%, 30% 0, 100% 0, 100% 100%, 0 100%);
    }

    .hidden-input {
        display: none;
    }

    .table-placeholder {
        padding: 16px;
        color: var(--muted);
    }

    .recommend-panel {
        display: none;
    }

    .recommend-panel.active {
        display: block;
    }

    .recommend-actions {
        display: flex;
        gap: 8px;
        align-items: center;
        flex-wrap: wrap;
        white-space: normal;
        word-break: keep-all;
        overflow-wrap: break-word;
    }

    .recommend-sort {
        display: inline-flex;
        gap: 6px;
        align-items: center;
        white-space: normal;
        word-break: keep-all;
        overflow-wrap: break-word;
    }

    .recommend-sort button.active {
        background: #111827;
        border-color: #111827;
        color: #fff;
    }

    .recommend-summary {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
    }

    .recommend-badge {
        display: inline-flex;
        align-items: center;
        border-radius: 999px;
        padding: 4px 10px;
        font-size: 12px;
        font-weight: 700;
        background: #f3f4f6;
        color: #111827;
    }

    .recommend-badge.good {
        background: #ecfdf3;
        color: #166534;
    }

    .recommend-badge.warn {
        background: #fff7ed;
        color: #9a3412;
    }

    .recommend-panel .panel-header h2 {
        margin: 0;
        white-space: normal;
        word-break: keep-all;
        overflow-wrap: break-word;
    }

    .suggest-field {
        position: relative;
        width: 132px;
        min-width: 132px;
    }

    .suggest-field > input[type="text"] {
        width: 100%;
        min-width: 0;
    }

    .detail-panel[data-status-panel="matched"] .filter-form input[name="wehago_amount"],
    .detail-panel[data-status-panel="matched"] .filter-form input[name="erp_amount"],
    .detail-panel[data-status-panel="amount_mismatch"] .filter-form input[name="wehago_amount"],
    .detail-panel[data-status-panel="amount_mismatch"] .filter-form input[name="erp_amount"] {
        width: 106px;
        min-width: 106px;
    }

    .detail-panel[data-status-panel="matched"] .filter-form .suggest-field:has(input[name="wehago_vendor"]),
    .detail-panel[data-status-panel="matched"] .filter-form .suggest-field:has(input[name="erp_vendor"]),
    .detail-panel[data-status-panel="amount_mismatch"] .filter-form .suggest-field:has(input[name="wehago_vendor"]),
    .detail-panel[data-status-panel="amount_mismatch"] .filter-form .suggest-field:has(input[name="erp_vendor"]) {
        flex: 1 1 220px;
        width: auto;
        min-width: 220px;
    }

    .detail-panel[data-status-panel="ledger_only"] .filter-form .suggest-field,
    .detail-panel[data-status-panel="voucher_only"] .filter-form .suggest-field,
    .detail-panel[data-status-panel="ledger_only"] .filter-form input[name="amount_keyword"],
    .detail-panel[data-status-panel="voucher_only"] .filter-form input[name="amount_keyword"] {
        width: 140px;
        min-width: 140px;
    }

    .suggest-dropdown {
        position: absolute;
        top: calc(100% + 4px);
        left: 0;
        right: auto;
        width: 100%;
        min-width: 100%;
        max-width: 100%;
        box-sizing: border-box;
        background: #fff;
        border: 1px solid var(--line);
        border-radius: 10px;
        box-shadow: 0 12px 24px rgba(15, 23, 42, 0.14);
        max-height: 260px;
        overflow: auto;
        z-index: 50;
        display: none;
    }

    .suggest-dropdown.open {
        display: block;
    }

    .suggest-item {
        width: 100%;
        text-align: left;
        padding: 3px 4px;
        margin: 0;
        border: 0;
        border-bottom: 1px solid rgba(148, 163, 184, 0.2);
        background: #fff;
        color: #111827;
        box-shadow: none;
        border-radius: 0;
        min-height: 28px;
        font-weight: 600;
        display: flex;
        align-items: center;
        gap: 6px;
        cursor: pointer;
        line-height: 1.2;
    }

    .suggest-item:last-child {
        border-bottom: 0;
    }

    .suggest-item:hover {
        background: #f3f4f6;
        transform: none;
    }

    .suggest-item input[type="checkbox"] {
        width: 12px !important;
        height: 12px !important;
        min-width: 12px !important;
        min-height: 12px !important;
        max-width: 12px !important;
        max-height: 12px !important;
        margin: 0 !important;
        padding: 0 !important;
        border: 0 !important;
        box-shadow: none !important;
        line-height: 1 !important;
        flex: 0 0 12px !important;
        accent-color: #111827;
        pointer-events: none;
    }

    .suggest-item .text {
        min-width: 0;
        flex: 1 1 auto;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .suggest-empty {
        padding: 10px 12px;
        color: var(--muted);
        font-size: 13px;
    }

    body[data-view-mode="dual"] .voucher-page:has(#pairRecommendPanel.active) {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        align-items: start;
        column-gap: 12px;
    }

    body[data-view-mode="dual"] .voucher-page:has(#pairRecommendPanel.active) > .voucher-topbar {
        grid-column: 1 / -1;
    }

    body[data-view-mode="dual"] .voucher-page:has(#pairRecommendPanel.active) > .panel-shell:not(#pairRecommendPanel) {
        grid-column: 1;
        min-width: 0;
    }

    body[data-view-mode="dual"] .voucher-page:has(#pairRecommendPanel.active) > #pairRecommendPanel.active {
        grid-column: 2;
        grid-row: 2;
        display: grid;
        grid-template-rows: auto auto minmax(0, 1fr);
        position: sticky;
        top: 10px;
        max-height: calc(100vh - 90px);
        min-width: 0;
    }

    body[data-view-mode="dual"] #pairRecommendPanel.active .table-wrap {
        max-height: calc(100vh - 230px);
    }

    body[data-view-mode="dual"] .detail-panel .table-wrap {
        max-height: clamp(300px, 44vh, 520px);
    }

    @media (max-width: 720px) {
        .voucher-topbar,
        .panel-header,
        .data-panel summary {
            align-items: flex-start;
        }

        .status-grid {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
        }

        .status-card {
            min-width: 0;
        }

        .cell-ellipsis {
            max-width: 180px;
        }
    }
</style>
{% endblock %}

{% block content %}
<section
    class="voucher-page"
    data-start-year="{{ wehago_compare.selected_start_year or '' }}"
    data-end-year="{{ wehago_compare.selected_end_year or '' }}"
>
    <datalist id="wehagoAccountOptions">
        {% for item in wehago_compare.wehago_account_options %}
        <option value="{{ item }}"></option>
        {% endfor %}
    </datalist>
    <datalist id="erpAccountOptions">
        {% for item in wehago_compare.erp_account_options %}
        <option value="{{ item }}"></option>
        {% endfor %}
    </datalist>
    <div class="voucher-topbar">
        <div class="voucher-title-wrap">
            <div class="section-title" style="margin-bottom: 0;">
                <h2>{{ wehago_compare.page_title }}</h2>
            </div>
        </div>
        <div class="voucher-toolbar">
            <form method="get" action="/wehago-compare">
                <label for="start_year">기간</label>
                <select id="start_year" name="start_year">
                    {% for year in wehago_compare.available_years %}
                    <option
                        value="{{ year }}"
                        {% if wehago_compare.selected_start_year == year %}selected{% endif %}
                        {% if wehago_compare.selected_end_year and year > wehago_compare.selected_end_year %}disabled{% endif %}
                    >{{ year }}</option>
                    {% endfor %}
                </select>
                <span>~</span>
                <select id="end_year" name="end_year">
                    {% for year in wehago_compare.available_years %}
                    <option
                        value="{{ year }}"
                        {% if wehago_compare.selected_end_year == year %}selected{% endif %}
                        {% if wehago_compare.selected_start_year and year < wehago_compare.selected_start_year %}disabled{% endif %}
                    >{{ year }}</option>
                    {% endfor %}
                </select>
                <button type="submit" class="button-secondary">적용</button>
            </form>
            <form method="post" action="/wehago-compare/refresh?start_year={{ wehago_compare.selected_start_year or '' }}&end_year={{ wehago_compare.selected_end_year or '' }}">
                <button type="submit">WEHAGO 새로 읽기</button>
            </form>
            <button type="button" class="button-secondary" id="undoLastActionBtn">직전 작업 취소</button>
            <span class="panel-meta" id="lastActionMeta">
                {% if wehago_compare.last_action %}
                최근 작업: {{ wehago_compare.last_action.action_type }} / {{ "{:,}".format(wehago_compare.last_action.count or 0) }}건
                {% else %}
                최근 작업: 없음
                {% endif %}
            </span>
        </div>
    </div>

    <section class="panel-shell">
        <div class="panel-header">
            <div><h2>현황</h2></div>
            <div class="panel-meta">
                기간 <strong>{{ wehago_compare.selected_start_year or '-' }} ~ {{ wehago_compare.selected_end_year or '-' }}</strong>
            </div>
        </div>
        <div class="panel-body">
            <div class="status-grid">
                {% for section in wehago_compare.metric_sections %}
                <button
                    type="button"
                    class="status-card"
                    data-target="detail-{{ section.key }}"
                    data-status="{{ section.key }}"
                    aria-expanded="false"
                >
                    <span class="status-label">{{ section.label }}</span>
                    <span class="status-value">{{ "{:,}".format(section.count) }}</span>
                </button>
                {% endfor %}
            </div>

            <div class="detail-stack">
                {% for section in wehago_compare.metric_sections %}
                <section class="detail-panel" id="detail-{{ section.key }}" data-status-panel="{{ section.key }}">
                    <div class="detail-head">
                        <strong>{{ section.label }}</strong>
                        {% if section.key in ['matched', 'amount_mismatch'] %}
                        <form class="filter-form" data-filter-kind="status" data-status="{{ section.key }}">
                            <div class="suggest-field">
                                <input type="text" name="wehago_account" placeholder="WEHAGO 계정코드" autocomplete="off">
                                <div class="suggest-dropdown" data-wehago-account-suggest="{{ section.key }}"></div>
                            </div>
                            <div class="suggest-field">
                                <input type="text" name="erp_account" placeholder="Hanmac 계정코드" autocomplete="off">
                                <div class="suggest-dropdown" data-erp-account-suggest="{{ section.key }}"></div>
                            </div>
                            <div class="suggest-field">
                                <input type="text" name="voucher_no" placeholder="전표번호" autocomplete="off">
                                <div class="suggest-dropdown" data-voucher-suggest="{{ section.key }}"></div>
                            </div>
                            <div class="suggest-field">
                                <input type="text" name="draft_no" placeholder="가전표번호" autocomplete="off">
                                <div class="suggest-dropdown" data-draft-suggest="{{ section.key }}"></div>
                            </div>
                            <input type="text" name="wehago_amount" placeholder="WEHAGO 금액">
                            <input type="text" name="erp_amount" placeholder="Hanmac 금액">
                            <div class="suggest-field">
                                <input type="text" name="wehago_vendor" placeholder="WEHAGO 거래처" autocomplete="off">
                                <div class="suggest-dropdown" data-wehago-vendor-suggest="{{ section.key }}"></div>
                            </div>
                            <div class="suggest-field">
                                <input type="text" name="erp_vendor" placeholder="Hanmac 거래처" autocomplete="off">
                                <div class="suggest-dropdown" data-erp-vendor-suggest="{{ section.key }}"></div>
                            </div>
                            <button type="submit">조회</button>
                            {% if section.key == 'amount_mismatch' %}
                            <button type="button" class="button-secondary" data-review-save>검토확인</button>
                            {% endif %}
                        </form>
                        {% else %}
                        <form class="filter-form" data-filter-kind="status" data-status="{{ section.key }}">
                            <div class="suggest-field">
                                <input type="text" name="voucher_no" placeholder="전표번호" autocomplete="off">
                                <div class="suggest-dropdown" data-voucher-suggest="{{ section.key }}"></div>
                            </div>
                            <div class="suggest-field">
                                <input type="text" name="account_keyword" placeholder="계정" autocomplete="off">
                                <div class="suggest-dropdown" data-account-suggest="{{ section.key }}"></div>
                            </div>
                            <div class="suggest-field">
                            <input type="text" name="vendor_keyword" placeholder="거래처" autocomplete="off">
                                <div class="suggest-dropdown" data-vendor-suggest="{{ section.key }}"></div>
                            </div>
                            <input type="text" name="amount_keyword" placeholder="금액">
                            <input type="text" name="desc_keyword" placeholder="적요">
                            <button type="submit">조회</button>
                            {% if section.key in ['ledger_only', 'voucher_only'] %}
                            <button type="button" class="button-secondary" data-pair-recommend>정확도 추천</button>
                            <button type="button" class="button-secondary" data-pair-save>개별 추천</button>
                            {% endif %}
                        </form>
                        {% endif %}
                        <div class="active-filter-cards" data-active-filters="{{ section.key }}"></div>
                        <div class="result-meta" data-result-meta="{{ section.key }}"></div>
                    </div>
                    <div class="table-wrap" data-table-wrap="{{ section.key }}">
                        <div class="table-placeholder">상태를 클릭하거나 조회를 실행하면 결과를 불러옵니다.</div>
                    </div>
                    <div class="table-footer" data-table-footer="{{ section.key }}" hidden>
                        <button type="button" class="button-secondary load-more-button" data-load-more="{{ section.key }}">더 보기</button>
                    </div>
                </section>
                {% endfor %}
            </div>
        </div>
    </section>

    <section class="panel-shell recommend-panel" id="pairRecommendPanel">
        <div class="panel-header">
            <div>
                <h2 id="recommendPanelTitle">정확도 추천 워크벤치</h2>
                <div class="panel-meta" id="recommendModeMeta"></div>
            </div>
            <div class="recommend-actions">
                <div class="recommend-sort" id="recommendSortTools">
                    <button type="button" class="button-secondary active" data-recommend-sort="score">점수순</button>
                    <button type="button" class="button-secondary" data-recommend-sort="amount_gap">금액차이순</button>
                    <button type="button" class="button-secondary" data-recommend-sort="account_similarity">계정유사도순</button>
                    <button type="button" class="button-secondary" data-recommend-sort="vendor_similarity">거래처유사도순</button>
                </div>
                <label id="recommendHighOnlyWrap" style="display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--muted);">
                    <input type="checkbox" id="recommendHighOnly">
                    고신뢰만 보기
                </label>
                <button type="button" class="button-secondary" id="recommendApplySelected">선택 저장</button>
                <button type="button" class="button-secondary" id="recommendApplyIndividual" hidden>선택 매칭 저장</button>
                <button type="button" class="button-secondary" id="recommendClose">닫기</button>
            </div>
        </div>
        <div class="panel-body">
            <div class="recommend-summary" id="recommendSummary"></div>
        </div>
        <div class="table-wrap" id="recommendTableWrap">
            <div class="table-placeholder">정확도 추천 버튼을 누르면 결과가 표시됩니다.</div>
        </div>
    </section>

</section>

<script>
    (() => {
        const page = document.querySelector('.voucher-page');
        const startYearValue = page?.dataset.startYear || '';
        const endYearValue = page?.dataset.endYear || '';

        const escapeHtml = (value) => String(value ?? '')
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');

        const escapeSelectorValue = (value) => String(value ?? '').replace(/["\\]/g, '\\$&');

        const detailState = new Map();
        const recheckSelections = new Map();
        const recheckRowStore = new Map();
        const pairSelections = new Map();
        const pairLedgerStore = new Map();
        const pairVoucherStore = new Map();
        const cumulativeFiltersByStatus = new Map();
        let latestPairRecommendation = null;

        const getCellClass = (field, monoFields = []) => {
            const classes = ['cell-ellipsis'];
            if (monoFields.includes(field)) {
                classes.push('mono');
            }
            if (['fiscal_year'].includes(field)) {
                classes.push('cell-tight');
            } else if (['voucher_no', 'draft_no', 'ledger_date', 'proof_date', 'confirmed_no', 'account_code'].includes(field)) {
                classes.push('cell-code');
            } else if (
                [
                    'ledger_account_name',
                    'voucher_account_name',
                    'ledger_vendor',
                    'voucher_vendor',
                    'ledger_debit',
                    'ledger_credit',
                    'voucher_debit',
                    'voucher_credit',
                ].includes(field)
            ) {
                classes.push('cell-equal');
            } else if (['ledger_desc', 'voucher_desc', 'desc1', 'desc2', 'description', 'review_reason', 'review_memo', 'notes'].includes(field)) {
                classes.push('cell-desc');
            }
            return classes.join(' ');
        };

        const renderTable = (container, payload, monoFields = [], append = false, statusKey = '') => {
            if (!container) {
                return;
            }
            const columns = payload.columns || [];
            const rows = payload.rows || [];
            if (!rows.length && !append) {
                container.innerHTML = '<div class="table-placeholder">조건에 맞는 항목이 없습니다.</div>';
                return;
            }
            const includeReviewCheckbox = statusKey === 'amount_mismatch';
            const includePairCheckbox = statusKey === 'ledger_only' || statusKey === 'voucher_only';
            const head = [
                (includeReviewCheckbox || includePairCheckbox) ? '<th></th>' : '',
                ...columns.map(([, label]) => `<th>${escapeHtml(label)}</th>`),
            ].join('');
            const body = rows.map((row) => {
                const reviewKey = row.review_key || '';
                if (includeReviewCheckbox && reviewKey) {
                    recheckRowStore.set(reviewKey, row);
                }
                const pairKey = statusKey === 'ledger_only'
                    ? (row.ledger_row_key || '')
                    : statusKey === 'voucher_only'
                        ? (row.voucher_row_key || '')
                        : '';
                if (includePairCheckbox && pairKey) {
                    if (statusKey === 'ledger_only') {
                        pairLedgerStore.set(pairKey, row);
                    } else {
                        pairVoucherStore.set(pairKey, row);
                    }
                }
                const cells = columns.map(([field]) => {
                    const raw = row[field] ?? '';
                    let display = '';
                    if (field === 'fiscal_year') {
                        display = String(raw ?? '');
                    } else if (typeof raw === 'number') {
                        display = Number.isInteger(raw)
                            ? raw.toLocaleString()
                            : raw.toLocaleString(undefined, { maximumFractionDigits: 2 });
                    } else {
                        display = String(raw);
                    }
                    const className = getCellClass(field, monoFields);
                    return `<td title="${escapeHtml(display)}"><span class="${className}">${escapeHtml(display)}</span></td>`;
                }).join('');
                const checkboxCell = includeReviewCheckbox
                    ? `<td><input type="checkbox" data-review-key="${escapeHtml(reviewKey)}" ${recheckSelections.get(reviewKey) ? 'checked' : ''}></td>`
                    : includePairCheckbox
                        ? `<td><input type="checkbox" data-pair-key="${escapeHtml(pairKey)}" data-pair-status="${escapeHtml(statusKey)}" ${pairSelections.get(pairKey) ? 'checked' : ''}></td>`
                        : '';
                return `<tr>${checkboxCell}${cells}</tr>`;
            }).join('');
            if (!append || !container.querySelector('table')) {
                container.innerHTML = `<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`;
            } else {
                const tbody = container.querySelector('tbody');
                if (tbody && body) {
                    tbody.insertAdjacentHTML('beforeend', body);
                }
            }
            if (includeReviewCheckbox) {
                container.querySelectorAll('input[data-review-key]').forEach((checkbox) => {
                    checkbox.addEventListener('change', () => {
                        const key = checkbox.dataset.reviewKey || '';
                        if (!key) return;
                        recheckSelections.set(key, checkbox.checked);
                    });
                });
            }
            if (includePairCheckbox) {
                container.querySelectorAll('input[data-pair-key]').forEach((checkbox) => {
                    checkbox.addEventListener('change', () => {
                        const key = checkbox.dataset.pairKey || '';
                        if (!key) return;
                        pairSelections.set(key, checkbox.checked);
                    });
                });
            }
        };

        const setMeta = (key, totalCount, shownCount, notice = '') => {
            const target = document.querySelector(`[data-result-meta="${key}"]`);
            if (target) {
                const countText = `조회 결과 ${Number(totalCount || 0).toLocaleString()}건`;
                const shownText = shownCount
                    ? ` / 현재 ${Number(shownCount || 0).toLocaleString()}건`
                    : '';
                const noticeText = notice ? ` / ${notice}` : '';
                target.textContent = `${countText}${shownText}${noticeText}`;
            }
        };

        const fetchJson = async (url) => {
            const response = await fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
            const payload = await response.json();
            if (!response.ok) {
                throw new Error(payload.error || '조회 중 오류가 발생했습니다.');
            }
            return payload;
        };

        const buildQuery = (form, extra = {}) => {
            const params = new URLSearchParams();
            if (startYearValue) params.set('start_year', startYearValue);
            if (endYearValue) params.set('end_year', endYearValue);
            if (form) {
                const data = new FormData(form);
                for (const [key, value] of data.entries()) {
                    if (String(value).trim()) {
                        params.set(key, String(value).trim());
                    }
                }
            }
            Object.entries(extra).forEach(([key, value]) => {
                if (value) params.set(key, value);
            });
            return params.toString();
        };

        const getStatusForm = (statusKey) => document.querySelector(`form[data-filter-kind="status"][data-status="${statusKey}"]`);
        const suggestState = new Map();
        const recommendPanel = document.getElementById('pairRecommendPanel');
        const recommendPanelTitle = document.getElementById('recommendPanelTitle');
        const recommendModeMeta = document.getElementById('recommendModeMeta');
        const recommendSummary = document.getElementById('recommendSummary');
        const recommendTableWrap = document.getElementById('recommendTableWrap');
        const recommendHighOnly = document.getElementById('recommendHighOnly');
        const recommendHighOnlyWrap = document.getElementById('recommendHighOnlyWrap');
        const recommendApplySelected = document.getElementById('recommendApplySelected');
        const recommendApplyIndividual = document.getElementById('recommendApplyIndividual');
        const recommendClose = document.getElementById('recommendClose');
        const recommendSortTools = document.getElementById('recommendSortTools');
        const recommendSortButtons = Array.from(document.querySelectorAll('[data-recommend-sort]'));
        const undoLastActionBtn = document.getElementById('undoLastActionBtn');
        const lastActionMeta = document.getElementById('lastActionMeta');
        const recommendSelections = new Map();
        let recommendSortKey = 'score';
        let recommendMode = 'batch';
        const individualState = {
            sourceStatus: '',
            sourceRow: null,
            sourceRowKey: '',
            rows: [],
            totalCount: 0,
            nextOffset: 0,
            hasMore: false,
            loading: false,
            selectedPairKey: '',
        };

        const refreshLastActionMeta = async () => {
            if (!lastActionMeta || !undoLastActionBtn) return;
            try {
                const payload = await fetchJson('/wehago-compare/api/last-action');
                const hasAction = Boolean(payload && payload.id);
                undoLastActionBtn.disabled = !hasAction;
                if (!hasAction) {
                    lastActionMeta.textContent = '최근 작업: 없음';
                    return;
                }
                const actionTypeMap = {
                    recheck_save: 'recheck 저장',
                    pair_save: '쌍매칭 저장',
                };
                const actionLabel = actionTypeMap[payload.action_type] || payload.action_type || '작업';
                lastActionMeta.textContent = `최근 작업: ${actionLabel} / ${formatNumber(payload.count || 0)}건`;
            } catch (_error) {
                undoLastActionBtn.disabled = true;
                lastActionMeta.textContent = '최근 작업: 조회 실패';
            }
        };

        const buildPairQuery = (extra = {}) => {
            const ledgerForm = getStatusForm('ledger_only');
            const voucherForm = getStatusForm('voucher_only');
            const params = new URLSearchParams();
            if (startYearValue) params.set('start_year', startYearValue);
            if (endYearValue) params.set('end_year', endYearValue);
            if (ledgerForm) {
                const data = new FormData(ledgerForm);
                params.set('ledger_voucher_no', String(data.get('voucher_no') || '').trim());
                params.set('ledger_review_reason', String(data.get('desc_keyword') || '').trim());
            }
            if (voucherForm) {
                const data = new FormData(voucherForm);
                params.set('voucher_voucher_no', String(data.get('voucher_no') || '').trim());
                params.set('voucher_review_reason', String(data.get('desc_keyword') || '').trim());
            }
            Object.entries(extra).forEach(([key, value]) => {
                if (value !== undefined && value !== null && String(value).trim() !== '') {
                    params.set(key, String(value).trim());
                }
            });
            return params;
        };

        const initStatusSuggest = (form, type) => {
            if (!form) return;
            const status = form.dataset.status || '';
            if (!['matched', 'amount_mismatch', 'ledger_only', 'voucher_only'].includes(status)) return;
            const inputName = type === 'account'
                ? 'account_keyword'
                : type === 'wehago_account'
                    ? 'wehago_account'
                    : type === 'erp_account'
                        ? 'erp_account'
                        : type === 'draft'
                            ? 'draft_no'
                : type === 'vendor'
                    ? 'vendor_keyword'
                    : type === 'wehago_vendor'
                        ? 'wehago_vendor'
                        : type === 'erp_vendor'
                            ? 'erp_vendor'
                    : 'voucher_no';
            const fieldName = type === 'account'
                ? 'account'
                : type === 'wehago_account'
                    ? 'wehago_account'
                    : type === 'erp_account'
                        ? 'erp_account'
                        : type === 'draft'
                            ? 'draft_no'
                : type === 'vendor'
                    ? 'vendor'
                    : type === 'wehago_vendor'
                        ? 'wehago_vendor'
                        : type === 'erp_vendor'
                            ? 'erp_vendor'
                    : 'voucher_no';
            const dropdownSelector = type === 'account'
                ? `[data-account-suggest="${status}"]`
                : type === 'wehago_account'
                    ? `[data-wehago-account-suggest="${status}"]`
                    : type === 'erp_account'
                        ? `[data-erp-account-suggest="${status}"]`
                        : type === 'draft'
                            ? `[data-draft-suggest="${status}"]`
                : type === 'vendor'
                    ? `[data-vendor-suggest="${status}"]`
                    : type === 'wehago_vendor'
                        ? `[data-wehago-vendor-suggest="${status}"]`
                        : type === 'erp_vendor'
                            ? `[data-erp-vendor-suggest="${status}"]`
                    : `[data-voucher-suggest="${status}"]`;
            const input = form.querySelector(`input[name="${inputName}"]`);
            const dropdown = form.querySelector(dropdownSelector);
            if (!input || !dropdown) return;
            const stateKey = `${status}:${type}`;
            suggestState.set(stateKey, { offset: 0, hasMore: false, loading: false, keyword: '' });

            const renderSuggest = (rows, append = false) => {
                if (!append) {
                    dropdown.innerHTML = '';
                }
                if (!rows.length && !append) {
                    dropdown.innerHTML = '<div class="suggest-empty">검색 결과가 없습니다.</div>';
                    dropdown.classList.add('open');
                    return;
                }
                const active = cumulativeFiltersByStatus.get(status) || {};
                const html = rows.map((row) => {
                    const value = type === 'voucher' ? (row.voucher_no || '') : (row.label || '');
                    const checked = String(active[inputName] || '') === String(value || '');
                    return `
                        <label class="suggest-item" title="${escapeHtml(row.label || '')}">
                            <input type="checkbox" data-suggest-check="${escapeHtml(inputName)}" data-suggest-value="${escapeHtml(value)}" ${checked ? 'checked' : ''}>
                            <span class="text">${escapeHtml(row.label || '')}</span>
                        </label>
                    `;
                }).join('');
                if (!append) {
                    dropdown.innerHTML = html;
                } else {
                    dropdown.insertAdjacentHTML('beforeend', html);
                }
                dropdown.classList.add('open');
                dropdown.querySelectorAll('input[type="checkbox"][data-suggest-check]').forEach((checkbox) => {
                    if (checkbox.dataset.bound === 'y') return;
                    checkbox.dataset.bound = 'y';
                    checkbox.addEventListener('change', () => {
                        const value = checkbox.dataset.suggestValue || '';
                        if (checkbox.checked) {
                            dropdown.querySelectorAll(`input[type="checkbox"][data-suggest-check="${escapeSelectorValue(inputName)}"]`).forEach((other) => {
                                if (other !== checkbox) other.checked = false;
                            });
                            input.value = value;
                            updateCumulativeFilter(status, inputName, value, true);
                        } else {
                            if (String(input.value || '').trim() === value) {
                                input.value = '';
                            }
                            updateCumulativeFilter(status, inputName, '', false);
                        }
                    });
                });
            };

            const loadSuggest = async (append = false) => {
                const state = suggestState.get(stateKey) || { offset: 0, hasMore: false, loading: false, keyword: '' };
                if (state.loading) return;
                const keyword = String(input.value || '').trim();
                if (!keyword) {
                    dropdown.classList.remove('open');
                    dropdown.innerHTML = '';
                    suggestState.set(stateKey, { ...state, offset: 0, hasMore: false, keyword: '' });
                    return;
                }
                const nextOffset = append ? (state.offset || 0) : 0;
                suggestState.set(stateKey, { ...state, loading: true, keyword });
                try {
                    const params = new URLSearchParams();
                    if (startYearValue) params.set('start_year', startYearValue);
                    if (endYearValue) params.set('end_year', endYearValue);
                    params.set('status', status);
                    params.set('field', fieldName);
                    params.set('keyword', keyword);
                    params.set('offset', String(nextOffset));
                    params.set('limit', '10');
                    let payload = await fetchJson(`/wehago-compare/api/status-suggestions?${params.toString()}`);
                    let rows = payload.rows || [];
                    if (!append && rows.length === 0) {
                        const fallbackParams = new URLSearchParams();
                        if (startYearValue) fallbackParams.set('start_year', startYearValue);
                        if (endYearValue) fallbackParams.set('end_year', endYearValue);
                        fallbackParams.set('status', status);
                        fallbackParams.set('field', fieldName);
                        fallbackParams.set('keyword', '');
                        fallbackParams.set('offset', '0');
                        fallbackParams.set('limit', '10');
                        const fallback = await fetchJson(`/wehago-compare/api/status-suggestions?${fallbackParams.toString()}`);
                        rows = fallback.rows || [];
                        payload = rows.length ? fallback : payload;
                    }
                    renderSuggest(rows, append);
                    suggestState.set(stateKey, {
                        ...state,
                        loading: false,
                        offset: Number(payload.next_offset || 0),
                        hasMore: Boolean(payload.has_more),
                        keyword,
                    });
                } catch (_error) {
                    suggestState.set(stateKey, { ...state, loading: false });
                }
            };

            let debounceTimer = null;
            input.addEventListener('input', () => {
                if (debounceTimer) {
                    window.clearTimeout(debounceTimer);
                }
                debounceTimer = window.setTimeout(() => {
                    loadSuggest(false);
                }, 180);
            });
            input.addEventListener('focus', () => {
                if (String(input.value || '').trim()) {
                    loadSuggest(false);
                }
            });
            dropdown.addEventListener('scroll', () => {
                const state = suggestState.get(stateKey);
                if (!state || !state.hasMore || state.loading) return;
                const remain = dropdown.scrollHeight - dropdown.scrollTop - dropdown.clientHeight;
                if (remain <= 40) {
                    loadSuggest(true);
                }
            });
            document.addEventListener('mousedown', (event) => {
                if (!form.contains(event.target)) {
                    dropdown.classList.remove('open');
                }
            });
        };

        const formatNumber = (value) => {
            const numberValue = Number(value || 0);
            return Number.isInteger(numberValue)
                ? numberValue.toLocaleString()
                : numberValue.toLocaleString(undefined, { maximumFractionDigits: 2 });
        };

        const prettyFilterLabels = {
            voucher_no: '전표번호',
            draft_no: '가전표번호',
            wehago_account: 'WEHAGO 계정',
            erp_account: 'Hanmac 계정',
            wehago_amount: 'WEHAGO 금액',
            erp_amount: 'Hanmac 금액',
            wehago_vendor: 'WEHAGO 거래처',
            erp_vendor: 'Hanmac 거래처',
            account_keyword: '계정',
            vendor_keyword: '거래처',
            amount_keyword: '금액',
            desc_keyword: '적요',
        };

        const getFormFieldValues = (form) => {
            const values = {};
            form.querySelectorAll('input[name]').forEach((input) => {
                const name = input.name;
                if (!name) return;
                const value = String(input.value || '').trim();
                if (!value) return;
                values[name] = value;
            });
            return values;
        };

        const updateCumulativeFilter = (statusKey, fieldName, value, checked = true) => {
            const current = { ...(cumulativeFiltersByStatus.get(statusKey) || {}) };
            if (checked && String(value || '').trim()) {
                current[fieldName] = String(value).trim();
            } else {
                delete current[fieldName];
            }
            cumulativeFiltersByStatus.set(statusKey, current);
            renderActiveFilterCards(statusKey);
        };

        const renderActiveFilterCards = (statusKey) => {
            const box = document.querySelector(`[data-active-filters="${statusKey}"]`);
            if (!box) return;
            const active = cumulativeFiltersByStatus.get(statusKey) || {};
            const entries = Object.entries(active).filter(([, value]) => String(value || '').trim() !== '');
            if (!entries.length) {
                box.innerHTML = '';
                return;
            }
            box.innerHTML = entries.map(([name, value]) => `
                <div class="active-filter-card">
                    <div class="value" title="${escapeHtml(`${prettyFilterLabels[name] || name}: ${value}`)}">${escapeHtml(`${prettyFilterLabels[name] || name}: ${value}`)}</div>
                    <button type="button" class="remove" data-remove-active-filter="${escapeHtml(statusKey)}" data-filter-name="${escapeHtml(name)}" aria-label="조건 제거">×</button>
                </div>
            `).join('');
        };

        const getFilteredRecommendations = () => {
            if (!latestPairRecommendation || !Array.isArray(latestPairRecommendation.pairs)) {
                return [];
            }
            const onlyHigh = Boolean(recommendHighOnly?.checked);
            const filtered = latestPairRecommendation.pairs.filter((pair) => !onlyHigh || pair.confidence_level === 'high');
            filtered.sort((a, b) => {
                if (recommendSortKey === 'amount_gap') {
                    const left = Number(a.amount_gap || 0);
                    const right = Number(b.amount_gap || 0);
                    return left - right;
                }
                if (recommendSortKey === 'account_similarity') {
                    const left = Number(a.account_similarity || 0);
                    const right = Number(b.account_similarity || 0);
                    return right - left;
                }
                if (recommendSortKey === 'vendor_similarity') {
                    const left = Number(a.vendor_similarity || 0);
                    const right = Number(b.vendor_similarity || 0);
                    return right - left;
                }
                return Number(b.score || 0) - Number(a.score || 0);
            });
            return filtered;
        };

        const renderRecommendationSummary = (stats = {}) => {
            if (!recommendSummary) {
                return;
            }
            recommendSummary.innerHTML = `
                <span class="recommend-badge">추천 ${formatNumber(stats.recommended || 0)}건</span>
                <span class="recommend-badge good">고신뢰 ${formatNumber(stats.high_confidence || 0)}건</span>
                <span class="recommend-badge">WEHAGO 풀 ${formatNumber(stats.ledger_rows || 0)}건</span>
                <span class="recommend-badge">ERP 풀 ${formatNumber(stats.voucher_rows || 0)}건</span>
            `;
        };

        const renderRecommendationTable = () => {
            if (!recommendTableWrap) {
                return;
            }
            const pairs = getFilteredRecommendations();
            if (!pairs.length) {
                recommendTableWrap.innerHTML = '<div class="table-placeholder">표시할 추천 결과가 없습니다.</div>';
                return;
            }
            const head = `
                <tr>
                    <th><input type="checkbox" id="recommendSelectAll"></th>
                    <th>점수</th>
                    <th>금액차이</th>
                    <th>계정유사도</th>
                    <th>거래처유사도</th>
                    <th>신뢰도</th>
                    <th>연도</th>
                    <th>전표번호</th>
                    <th>일자</th>
                    <th>가전표번호</th>
                    <th>WEHAGO 계정</th>
                    <th>ERP 계정</th>
                    <th>WEHAGO 거래처</th>
                    <th>ERP 거래처</th>
                    <th>WEHAGO 금액</th>
                    <th>ERP 금액</th>
                    <th>WEHAGO 적요</th>
                    <th>ERP 적요</th>
                </tr>
            `;
            const body = pairs.map((pair) => {
                const key = pair.pair_key || '';
                const ledger = pair.ledger_row || {};
                const voucher = pair.voucher_row || {};
                const ledgerAmount = Math.max(Number(ledger.ledger_debit || 0), Number(ledger.ledger_credit || 0));
                const voucherAmount = Math.max(Number(voucher.voucher_debit || 0), Number(voucher.voucher_credit || 0));
                if (!recommendSelections.has(key) && pair.auto_eligible) {
                    recommendSelections.set(key, true);
                }
                const checked = recommendSelections.get(key) ? 'checked' : '';
                return `
                    <tr>
                        <td><input type="checkbox" data-recommend-key="${escapeHtml(key)}" ${checked}></td>
                        <td title="${escapeHtml(pair.score)}"><span class="cell-ellipsis cell-tight">${escapeHtml(pair.score)}</span></td>
                        <td title="${escapeHtml(formatNumber(pair.amount_gap || 0))}"><span class="cell-ellipsis cell-tight">${escapeHtml(formatNumber(pair.amount_gap || 0))}</span></td>
                        <td title="${escapeHtml((Number(pair.account_similarity || 0) * 100).toFixed(1) + '%')}"><span class="cell-ellipsis cell-tight">${escapeHtml((Number(pair.account_similarity || 0) * 100).toFixed(1) + '%')}</span></td>
                        <td title="${escapeHtml((Number(pair.vendor_similarity || 0) * 100).toFixed(1) + '%')}"><span class="cell-ellipsis cell-tight">${escapeHtml((Number(pair.vendor_similarity || 0) * 100).toFixed(1) + '%')}</span></td>
                        <td title="${escapeHtml(pair.confidence_level)}"><span class="cell-ellipsis cell-tight">${escapeHtml(pair.confidence_level)}</span></td>
                        <td title="${escapeHtml(ledger.fiscal_year || voucher.fiscal_year || '')}"><span class="cell-ellipsis cell-tight">${escapeHtml(ledger.fiscal_year || voucher.fiscal_year || '')}</span></td>
                        <td title="${escapeHtml(ledger.voucher_no || '')}"><span class="cell-ellipsis cell-code">${escapeHtml(ledger.voucher_no || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_date || '')}"><span class="cell-ellipsis cell-code">${escapeHtml(ledger.ledger_date || '')}</span></td>
                        <td title="${escapeHtml(voucher.draft_no || '')}"><span class="cell-ellipsis cell-code">${escapeHtml(voucher.draft_no || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_account_name || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(ledger.ledger_account_name || '')}</span></td>
                        <td title="${escapeHtml(voucher.voucher_account_name || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(voucher.voucher_account_name || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_vendor || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(ledger.ledger_vendor || '')}</span></td>
                        <td title="${escapeHtml(voucher.voucher_vendor || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(voucher.voucher_vendor || '')}</span></td>
                        <td title="${escapeHtml(formatNumber(ledgerAmount))}"><span class="cell-ellipsis cell-equal">${escapeHtml(formatNumber(ledgerAmount))}</span></td>
                        <td title="${escapeHtml(formatNumber(voucherAmount))}"><span class="cell-ellipsis cell-equal">${escapeHtml(formatNumber(voucherAmount))}</span></td>
                        <td title="${escapeHtml(ledger.ledger_desc || '')}"><span class="cell-ellipsis cell-desc">${escapeHtml(ledger.ledger_desc || '')}</span></td>
                        <td title="${escapeHtml(voucher.voucher_desc || '')}"><span class="cell-ellipsis cell-desc">${escapeHtml(voucher.voucher_desc || '')}</span></td>
                    </tr>
                `;
            }).join('');
            recommendTableWrap.innerHTML = `<table><thead>${head}</thead><tbody>${body}</tbody></table>`;

            const selectAll = document.getElementById('recommendSelectAll');
            const rowChecks = Array.from(recommendTableWrap.querySelectorAll('input[data-recommend-key]'));
            const syncHeader = () => {
                if (!selectAll) return;
                const checkedCount = rowChecks.filter((el) => el.checked).length;
                selectAll.checked = rowChecks.length > 0 && checkedCount === rowChecks.length;
                selectAll.indeterminate = checkedCount > 0 && checkedCount < rowChecks.length;
            };
            rowChecks.forEach((checkbox) => {
                checkbox.addEventListener('change', () => {
                    const key = checkbox.dataset.recommendKey || '';
                    if (!key) return;
                    recommendSelections.set(key, checkbox.checked);
                    syncHeader();
                });
            });
            if (selectAll) {
                selectAll.addEventListener('change', () => {
                    rowChecks.forEach((checkbox) => {
                        checkbox.checked = selectAll.checked;
                        const key = checkbox.dataset.recommendKey || '';
                        if (key) {
                            recommendSelections.set(key, selectAll.checked);
                        }
                    });
                    syncHeader();
                });
            }
            syncHeader();
        };

        const setRecommendMode = (mode) => {
            recommendMode = mode === 'individual' ? 'individual' : 'batch';
            if (recommendPanelTitle) {
                recommendPanelTitle.textContent = recommendMode === 'individual' ? '개별 추천' : '정확도 추천 워크벤치';
            }
            if (recommendModeMeta && recommendMode === 'batch') {
                recommendModeMeta.textContent = '';
            }
            if (recommendSortTools) {
                recommendSortTools.hidden = recommendMode === 'individual';
            }
            if (recommendHighOnlyWrap) {
                recommendHighOnlyWrap.hidden = recommendMode === 'individual';
            }
            if (recommendApplySelected) {
                recommendApplySelected.hidden = recommendMode === 'individual';
            }
            if (recommendApplyIndividual) {
                recommendApplyIndividual.hidden = recommendMode !== 'individual';
            }
            if (recommendSummary) {
                recommendSummary.style.display = recommendMode === 'individual' ? 'none' : 'flex';
            }
        };

        const openRecommendationPanel = (mode = 'batch') => {
            if (!recommendPanel) return;
            setRecommendMode(mode);
            recommendPanel.classList.add('active');
            recommendPanel.scrollIntoView({ behavior: 'smooth', block: 'start' });
        };

        const closeRecommendationPanel = () => {
            if (!recommendPanel) return;
            recommendPanel.classList.remove('active');
        };

        const getSelectedSourceForIndividual = (sourceStatus) => {
            if (sourceStatus === 'ledger_only') {
                return Array.from(pairSelections.entries())
                    .filter(([key, checked]) => checked && pairLedgerStore.has(key))
                    .map(([key]) => pairLedgerStore.get(key))
                    .filter(Boolean);
            }
            return Array.from(pairSelections.entries())
                .filter(([key, checked]) => checked && pairVoucherStore.has(key))
                .map(([key]) => pairVoucherStore.get(key))
                .filter(Boolean);
        };

        const renderIndividualRows = (append = false) => {
            if (!recommendTableWrap) return;
            const rows = individualState.rows || [];
            if (!rows.length && !append) {
                recommendTableWrap.innerHTML = '<div class="table-placeholder">추천 항목이 없습니다.</div>';
                return;
            }
            const head = `
                <tr>
                    <th></th>
                    <th>점수</th>
                    <th>금액차이</th>
                    <th>계정유사도</th>
                    <th>거래처유사도</th>
                    <th>연도</th>
                    <th>전표번호</th>
                    <th>일자</th>
                    <th>가전표번호</th>
                    <th>WEHAGO 계정</th>
                    <th>ERP 계정</th>
                    <th>WEHAGO 거래처</th>
                    <th>ERP 거래처</th>
                    <th>WEHAGO 적요</th>
                    <th>ERP 적요</th>
                </tr>
            `;
            const body = rows.map((item) => {
                const target = item.target_row || {};
                const isLedgerSource = individualState.sourceStatus === 'ledger_only';
                const ledger = isLedgerSource ? (individualState.sourceRow || {}) : target;
                const voucher = isLedgerSource ? target : (individualState.sourceRow || {});
                const checked = individualState.selectedPairKey === item.pair_key ? 'checked' : '';
                return `
                    <tr>
                        <td><input type="radio" name="individualPairRadio" value="${escapeHtml(item.pair_key || '')}" ${checked}></td>
                        <td title="${escapeHtml(item.score)}"><span class="cell-ellipsis cell-tight">${escapeHtml(item.score)}</span></td>
                        <td title="${escapeHtml(formatNumber(item.amount_gap || 0))}"><span class="cell-ellipsis cell-tight">${escapeHtml(formatNumber(item.amount_gap || 0))}</span></td>
                        <td title="${escapeHtml((Number(item.account_similarity || 0) * 100).toFixed(1) + '%')}"><span class="cell-ellipsis cell-tight">${escapeHtml((Number(item.account_similarity || 0) * 100).toFixed(1) + '%')}</span></td>
                        <td title="${escapeHtml((Number(item.vendor_similarity || 0) * 100).toFixed(1) + '%')}"><span class="cell-ellipsis cell-tight">${escapeHtml((Number(item.vendor_similarity || 0) * 100).toFixed(1) + '%')}</span></td>
                        <td title="${escapeHtml(ledger.fiscal_year || voucher.fiscal_year || '')}"><span class="cell-ellipsis cell-tight">${escapeHtml(ledger.fiscal_year || voucher.fiscal_year || '')}</span></td>
                        <td title="${escapeHtml(ledger.voucher_no || '')}"><span class="cell-ellipsis cell-code">${escapeHtml(ledger.voucher_no || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_date || '')}"><span class="cell-ellipsis cell-code">${escapeHtml(ledger.ledger_date || '')}</span></td>
                        <td title="${escapeHtml(voucher.draft_no || '')}"><span class="cell-ellipsis cell-code">${escapeHtml(voucher.draft_no || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_account_name || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(ledger.ledger_account_name || '')}</span></td>
                        <td title="${escapeHtml(voucher.voucher_account_name || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(voucher.voucher_account_name || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_vendor || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(ledger.ledger_vendor || '')}</span></td>
                        <td title="${escapeHtml(voucher.voucher_vendor || '')}"><span class="cell-ellipsis cell-equal">${escapeHtml(voucher.voucher_vendor || '')}</span></td>
                        <td title="${escapeHtml(ledger.ledger_desc || '')}"><span class="cell-ellipsis cell-desc">${escapeHtml(ledger.ledger_desc || '')}</span></td>
                        <td title="${escapeHtml(voucher.voucher_desc || '')}"><span class="cell-ellipsis cell-desc">${escapeHtml(voucher.voucher_desc || '')}</span></td>
                    </tr>
                `;
            }).join('');
            recommendTableWrap.innerHTML = `<table><thead>${head}</thead><tbody>${body}</tbody></table>`;
            recommendTableWrap.querySelectorAll('input[name="individualPairRadio"]').forEach((radio) => {
                radio.addEventListener('change', () => {
                    individualState.selectedPairKey = radio.value || '';
                });
            });
        };

        const loadIndividualRecommendations = async (append = false) => {
            if (!individualState.sourceStatus || !individualState.sourceRowKey || individualState.loading) return;
            individualState.loading = true;
            if (!append && recommendTableWrap) {
                recommendTableWrap.innerHTML = '<div class="table-placeholder">개별 추천 불러오는 중...</div>';
            }
            try {
                const params = new URLSearchParams();
                if (startYearValue) params.set('start_year', startYearValue);
                if (endYearValue) params.set('end_year', endYearValue);
                params.set('source_status', individualState.sourceStatus);
                params.set('source_row_key', individualState.sourceRowKey);
                params.set('offset', append ? String(individualState.nextOffset || 0) : '0');
                params.set('limit', '10');
                const payload = await fetchJson(`/wehago-compare/api/pair-individual-recommendations?${params.toString()}`);
                if (!append) {
                    individualState.rows = payload.rows || [];
                    individualState.sourceRow = payload.source_row || null;
                    individualState.totalCount = Number(payload.total_count || 0);
                } else {
                    individualState.rows = [...(individualState.rows || []), ...(payload.rows || [])];
                }
                individualState.nextOffset = Number(payload.next_offset || 0);
                individualState.hasMore = Boolean(payload.has_more);
                if (recommendModeMeta) {
                    const source = individualState.sourceRow || {};
                    const label = individualState.sourceStatus === 'ledger_only' ? 'Unmatched 기준' : 'ERP Unmatched 기준';
                    recommendModeMeta.textContent = `${label} / 전표번호 ${source.voucher_no || '-'} / 계정 ${source.ledger_account_name || source.voucher_account_name || '-'}`;
                }
                if (!individualState.selectedPairKey && individualState.rows.length) {
                    individualState.selectedPairKey = individualState.rows[0].pair_key || '';
                }
                renderIndividualRows(append);
            } catch (error) {
                if (recommendTableWrap) {
                    recommendTableWrap.innerHTML = `<div class="table-placeholder">${escapeHtml(error.message)}</div>`;
                }
            } finally {
                individualState.loading = false;
            }
        };

        const setLoadMoreState = (statusKey, hasMore, loading = false) => {
            const footer = document.querySelector(`[data-table-footer="${statusKey}"]`);
            const button = document.querySelector(`[data-load-more="${statusKey}"]`);
            if (!footer || !button) return;
            footer.hidden = !hasMore && !loading;
            button.disabled = loading;
            button.textContent = loading ? '불러오는 중...' : '더 보기';
        };

        const buildStatusQuery = (form, statusKey, extra = {}) => {
            const selected = form ? (cumulativeFiltersByStatus.get(statusKey) || {}) : {};
            const mapped = new URLSearchParams();
            if (startYearValue) mapped.set('start_year', startYearValue);
            if (endYearValue) mapped.set('end_year', endYearValue);
            mapped.set('status', statusKey);
            const rawVoucher = String(selected.voucher_no || '').trim();
            const rawDesc = String(selected.desc_keyword || '').trim();
            if (rawVoucher) mapped.set('voucher_no', rawVoucher);
            if (rawDesc) mapped.set('desc_keyword', rawDesc);
            if (statusKey === 'ledger_only') {
                const account = String(selected.account_keyword || '').trim();
                const vendor = String(selected.vendor_keyword || '').trim();
                const amount = String(selected.amount_keyword || '').trim();
                if (account) mapped.set('wehago_account', account);
                if (vendor) mapped.set('wehago_vendor', vendor);
                if (amount) mapped.set('wehago_amount', amount);
            } else if (statusKey === 'voucher_only') {
                const account = String(selected.account_keyword || '').trim();
                const vendor = String(selected.vendor_keyword || '').trim();
                const amount = String(selected.amount_keyword || '').trim();
                if (account) mapped.set('erp_account', account);
                if (vendor) mapped.set('erp_vendor', vendor);
                if (amount) mapped.set('erp_amount', amount);
            } else {
                for (const [key, value] of Object.entries(selected)) {
                    if (String(value).trim()) {
                        mapped.set(key, String(value).trim());
                    }
                }
            }
            Object.entries(extra).forEach(([key, value]) => {
                if (value !== undefined && value !== null && String(value) !== '') {
                    mapped.set(key, String(value));
                }
            });
            return mapped.toString();
        };

        const loadStatusRows = async (statusKey, form = null, append = false) => {
            const wrap = document.querySelector(`[data-table-wrap="${statusKey}"]`);
            if (!wrap) return;
            const current = detailState.get(statusKey) || { offset: 0, totalCount: 0, loading: false };
            if (current.loading) return;
            if (!append && form) {
                const selected = getFormFieldValues(form);
                const merged = { ...(cumulativeFiltersByStatus.get(statusKey) || {}), ...selected };
                cumulativeFiltersByStatus.set(statusKey, merged);
                renderActiveFilterCards(statusKey);
            }
            const nextOffset = append ? (current.nextOffset || 0) : 0;
            detailState.set(statusKey, { ...current, loading: true, form });
            setLoadMoreState(statusKey, true, true);
            if (!append) {
                wrap.innerHTML = '<div class="table-placeholder">조회 중입니다...</div>';
            }
            try {
                const payload = await fetchJson(`/wehago-compare/api/status-rows?${buildStatusQuery(form, statusKey, { offset: nextOffset, limit: 200 })}`);
                renderTable(wrap, payload, ['voucher_no'], append, statusKey);
                const shownCount = append ? nextOffset + payload.shown_count : payload.shown_count;
                detailState.set(statusKey, {
                    offset: payload.offset,
                    nextOffset: payload.next_offset,
                    totalCount: payload.total_count,
                    hasMore: payload.has_more,
                    loading: false,
                    form,
                    shownCount,
                });
                setMeta(statusKey, payload.total_count, shownCount, payload.notice);
                setLoadMoreState(statusKey, payload.has_more, false);
            } catch (error) {
                if (!append) {
                    wrap.innerHTML = `<div class="table-placeholder">${escapeHtml(error.message)}</div>`;
                }
                detailState.set(statusKey, { ...current, loading: false, form });
                setLoadMoreState(statusKey, current.hasMore, false);
            }
        };

        const cards = Array.from(document.querySelectorAll('.status-card'));
        const panels = Array.from(document.querySelectorAll('.detail-panel'));
        cards.forEach((card) => {
            card.addEventListener('click', () => {
                // 상태 탭 전환 시 추천/개별 추천 패널은 즉시 닫는다.
                closeRecommendationPanel();
                const targetId = card.dataset.target;
                const statusKey = card.dataset.status;
                const panel = targetId ? document.getElementById(targetId) : null;
                const isActive = panel ? panel.classList.contains('active') : false;

                cards.forEach((item) => item.setAttribute('aria-expanded', 'false'));
                panels.forEach((item) => item.classList.remove('active'));

                if (panel && !isActive) {
                    panel.classList.add('active');
                    card.setAttribute('aria-expanded', 'true');
                    const form = panel.querySelector('form[data-filter-kind="status"]');
                    loadStatusRows(statusKey, form, false);
                }
            });
        });

        document.querySelectorAll('form[data-filter-kind="status"]').forEach((form) => {
            initStatusSuggest(form, 'voucher');
            initStatusSuggest(form, 'draft');
            initStatusSuggest(form, 'account');
            initStatusSuggest(form, 'vendor');
            initStatusSuggest(form, 'wehago_account');
            initStatusSuggest(form, 'erp_account');
            initStatusSuggest(form, 'wehago_vendor');
            initStatusSuggest(form, 'erp_vendor');
            form.addEventListener('submit', (event) => {
                event.preventDefault();
                const statusKey = form.dataset.status;
                loadStatusRows(statusKey, form, false);
            });
        });

        document.addEventListener('click', (event) => {
            const removeButton = event.target.closest('[data-remove-active-filter]');
            if (!removeButton) return;
            const statusKey = removeButton.dataset.removeActiveFilter || '';
            const filterName = removeButton.dataset.filterName || '';
            if (!statusKey || !filterName) return;
            const current = { ...(cumulativeFiltersByStatus.get(statusKey) || {}) };
            delete current[filterName];
            cumulativeFiltersByStatus.set(statusKey, current);
            renderActiveFilterCards(statusKey);
            const form = document.querySelector(`form[data-filter-kind="status"][data-status="${statusKey}"]`);
            if (form) {
                const safeName = escapeSelectorValue(filterName);
                const input = form.querySelector(`input[name="${safeName}"]`);
                if (input) input.value = '';
            }
            loadStatusRows(statusKey, form, false);
        });

        document.querySelectorAll('[data-load-more]').forEach((button) => {
            button.addEventListener('click', () => {
                const statusKey = button.dataset.loadMore;
                const state = detailState.get(statusKey);
                const form = state?.form || document.querySelector(`form[data-filter-kind="status"][data-status="${statusKey}"]`);
                loadStatusRows(statusKey, form, true);
            });
        });

        document.querySelectorAll('[data-review-save]').forEach((button) => {
            button.addEventListener('click', async () => {
                const selectedRows = Array.from(recheckSelections.entries())
                    .filter(([, checked]) => checked)
                    .map(([key]) => recheckRowStore.get(key))
                    .filter(Boolean);
                if (!selectedRows.length) {
                    alert('저장할 검토 항목을 선택해주세요.');
                    return;
                }
                button.disabled = true;
                try {
                    const response = await fetch('/wehago-compare/api/recheck-review-save', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest',
                        },
                        body: JSON.stringify({ rows: selectedRows }),
                    });
                    const payload = await response.json();
                    if (!response.ok) {
                        throw new Error(payload.error || '검토 저장 중 오류가 발생했습니다.');
                    }
                    window.location.reload();
                } catch (error) {
                    alert(error.message);
                } finally {
                    button.disabled = false;
                }
            });
        });

        document.querySelectorAll('[data-pair-save]').forEach((button) => {
            button.addEventListener('click', async () => {
                const form = button.closest('form[data-status]');
                const sourceStatus = form?.dataset.status || '';
                if (!['ledger_only', 'voucher_only'].includes(sourceStatus)) {
                    alert('개별 추천은 Unmatched 또는 ERP Unmatched에서만 사용할 수 있습니다.');
                    return;
                }
                const selectedRows = getSelectedSourceForIndividual(sourceStatus);
                if (!selectedRows.length) {
                    alert(sourceStatus === 'ledger_only' ? 'Unmatched에서 항목 1건을 체크해주세요.' : 'ERP Unmatched에서 항목 1건을 체크해주세요.');
                    return;
                }
                if (selectedRows.length > 1) {
                    alert('개별 추천은 한 번에 1건만 선택해서 실행해주세요.');
                    return;
                }
                const sourceRow = selectedRows[0];
                individualState.sourceStatus = sourceStatus;
                individualState.sourceRow = sourceRow;
                individualState.sourceRowKey = sourceStatus === 'ledger_only'
                    ? (sourceRow.ledger_row_key || '')
                    : (sourceRow.voucher_row_key || '');
                individualState.rows = [];
                individualState.nextOffset = 0;
                individualState.hasMore = false;
                individualState.selectedPairKey = '';
                button.disabled = true;
                try {
                    openRecommendationPanel('individual');
                    await loadIndividualRecommendations(false);
                } catch (error) {
                    alert(error.message);
                } finally {
                    button.disabled = false;
                }
            });
        });

        document.querySelectorAll('[data-pair-recommend]').forEach((button) => {
            button.addEventListener('click', async () => {
                button.disabled = true;
                try {
                    const params = buildPairQuery({ limit: 300 });
                    const payload = await fetchJson(`/wehago-compare/api/pair-recommendations?${params.toString()}`);
                    recommendSelections.clear();
                    latestPairRecommendation = payload;
                    openRecommendationPanel('batch');
                    renderRecommendationSummary(payload.stats || {});
                    renderRecommendationTable();
                    const stats = payload.stats || {};
                    const pairs = payload.pairs || [];
                    if (!pairs.length) {
                        recommendTableWrap.innerHTML = '<div class="table-placeholder">추천 가능한 쌍이 없습니다. 조회 조건을 조금 더 좁혀주세요.</div>';
                    }
                    if (stats.recommended) {
                        setMeta('ledger_only', stats.ledger_rows, undefined, `추천 ${Number(stats.recommended).toLocaleString()}건 준비됨`);
                        setMeta('voucher_only', stats.voucher_rows, undefined, `추천 ${Number(stats.recommended).toLocaleString()}건 준비됨`);
                    }
                } catch (error) {
                    alert(error.message);
                } finally {
                    button.disabled = false;
                }
            });
        });

        if (recommendHighOnly) {
            recommendHighOnly.addEventListener('change', () => {
                renderRecommendationTable();
            });
        }

        recommendSortButtons.forEach((button) => {
            button.addEventListener('click', () => {
                const key = button.dataset.recommendSort || 'score';
                recommendSortKey = key;
                recommendSortButtons.forEach((item) => {
                    item.classList.toggle('active', item === button);
                });
                renderRecommendationTable();
            });
        });

        if (recommendApplySelected) {
            recommendApplySelected.addEventListener('click', async () => {
                const keys = Array.from(recommendSelections.entries())
                    .filter(([, checked]) => checked)
                    .map(([key]) => key);
                if (!keys.length) {
                    alert('저장할 추천 항목을 선택해주세요.');
                    return;
                }
                recommendApplySelected.disabled = true;
                try {
                    const params = buildPairQuery();
                    const response = await fetch('/wehago-compare/api/pair-recommendations/apply', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest',
                        },
                        body: JSON.stringify({
                            start_year: startYearValue || null,
                            end_year: endYearValue || null,
                            pair_keys: keys,
                            auto_only: false,
                            ledger_voucher_no: params.get('ledger_voucher_no') || '',
                            ledger_review_reason: params.get('ledger_review_reason') || '',
                            voucher_voucher_no: params.get('voucher_voucher_no') || '',
                            voucher_review_reason: params.get('voucher_review_reason') || '',
                        }),
                    });
                    const payload = await response.json();
                    if (!response.ok) {
                        throw new Error(payload.error || '추천 선택 저장 중 오류가 발생했습니다.');
                    }
                    alert(`추천 선택 저장 완료: ${Number(payload.saved_count || 0).toLocaleString()}건`);
                    window.location.reload();
                } catch (error) {
                    alert(error.message);
                } finally {
                    recommendApplySelected.disabled = false;
                }
            });
        }

        if (recommendClose) {
            recommendClose.addEventListener('click', () => {
                closeRecommendationPanel();
            });
        }

        if (recommendApplyIndividual) {
            recommendApplyIndividual.addEventListener('click', async () => {
                const picked = (individualState.rows || []).find((row) => row.pair_key === individualState.selectedPairKey);
                if (!picked) {
                    alert('매칭할 추천 항목 1건을 선택해주세요.');
                    return;
                }
                recommendApplyIndividual.disabled = true;
                try {
                    const isLedgerSource = individualState.sourceStatus === 'ledger_only';
                    const ledgerRows = isLedgerSource ? [individualState.sourceRow] : [picked.target_row];
                    const voucherRows = isLedgerSource ? [picked.target_row] : [individualState.sourceRow];
                    const response = await fetch('/wehago-compare/api/pair-match-save', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest',
                        },
                        body: JSON.stringify({ ledger_rows: ledgerRows, voucher_rows: voucherRows }),
                    });
                    const payload = await response.json();
                    if (!response.ok) {
                        throw new Error(payload.error || '개별 매칭 저장 중 오류가 발생했습니다.');
                    }
                    alert(`개별 매칭 저장 완료: ${Number(payload.saved_count || 0).toLocaleString()}건`);
                    window.location.reload();
                } catch (error) {
                    alert(error.message);
                } finally {
                    recommendApplyIndividual.disabled = false;
                }
            });
        }

        if (recommendTableWrap) {
            recommendTableWrap.addEventListener('scroll', () => {
                if (recommendMode !== 'individual') return;
                if (!individualState.hasMore || individualState.loading) return;
                const remain = recommendTableWrap.scrollHeight - recommendTableWrap.scrollTop - recommendTableWrap.clientHeight;
                if (remain <= 100) {
                    loadIndividualRecommendations(true);
                }
            });
        }

        if (undoLastActionBtn) {
            undoLastActionBtn.addEventListener('click', async () => {
                const confirmed = window.confirm('직전 작업을 취소할까요? 최근 저장 작업 1건만 되돌립니다.');
                if (!confirmed) return;
                undoLastActionBtn.disabled = true;
                try {
                    const response = await fetch('/wehago-compare/api/undo-last-action', {
                        method: 'POST',
                        headers: { 'X-Requested-With': 'XMLHttpRequest' },
                    });
                    const payload = await response.json();
                    if (!response.ok) {
                        throw new Error(payload.error || '되돌리기 중 오류가 발생했습니다.');
                    }
                    if (!payload.undone) {
                        alert(payload.message || '되돌릴 작업이 없습니다.');
                    } else {
                        alert(`되돌리기 완료: ${formatNumber(payload.affected || 0)}건`);
                        window.location.reload();
                    }
                } catch (error) {
                    alert(error.message);
                } finally {
                    undoLastActionBtn.disabled = false;
                }
            });
        }

        document.querySelectorAll('[data-table-wrap]').forEach((wrap) => {
            const statusKey = wrap.dataset.tableWrap;
            if (!statusKey) {
                return;
            }
            wrap.addEventListener('scroll', () => {
                const state = detailState.get(statusKey);
                if (!state || state.loading || !state.hasMore) {
                    return;
                }
                const threshold = 120;
                const remain = wrap.scrollHeight - wrap.scrollTop - wrap.clientHeight;
                if (remain <= threshold) {
                    const form = state.form || document.querySelector(`form[data-filter-kind="status"][data-status="${statusKey}"]`);
                    loadStatusRows(statusKey, form, true);
                }
            });
        });

        const startYear = document.getElementById('start_year');
        const endYear = document.getElementById('end_year');
        const syncYearOptions = () => {
            if (!startYear || !endYear) {
                return;
            }
            const startValue = Number(startYear.value || 0);
            const endValue = Number(endYear.value || 0);
            Array.from(startYear.options).forEach((option) => {
                option.disabled = Boolean(endValue) && Number(option.value) > endValue;
            });
            Array.from(endYear.options).forEach((option) => {
                option.disabled = Boolean(startValue) && Number(option.value) < startValue;
            });
            if (startValue && endValue && startValue > endValue) {
                endYear.value = startYear.value;
            }
        };
        if (startYear && endYear) {
            startYear.addEventListener('change', syncYearOptions);
            endYear.addEventListener('change', syncYearOptions);
            syncYearOptions();
        }
        refreshLastActionMeta();
    })();
</script>
{% endblock %}
