@extends('admin.layouts.app')

@section('title', '관리자 메일 발송')
@section('page_title', '관리자 메일 발송')
@section('page_desc', '단건 / 여러건 / 템플릿(CSV) / DB검색 발송')

@push('head')
    <style>
        /* mail send page only */
        .mwrap{display:grid; grid-template-columns: 1fr 1.15fr; gap:16px;}
        @media (max-width: 1100px){ .mwrap{grid-template-columns:1fr;} }

        .mtop{display:flex; gap:12px; flex-wrap:wrap; align-items:end;}
        .mtop__grow{flex:1; min-width:240px;}
        .mtop__right{margin-left:auto; display:flex; gap:10px; align-items:center;}

        .mbtn{padding:8px 12px;font-size:13px;border-radius:12px;line-height:1.1;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:6px;
            border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.06);color:inherit;cursor:pointer;}
        .mbtn:hover{background:rgba(255,255,255,.10);}
        .mbtn--primary{background:rgba(59,130,246,.88);border-color:rgba(59,130,246,.95);color:#fff;}
        .mbtn--primary:hover{background:rgba(59,130,246,.98);}
        .mbtn--danger{background:rgba(244,63,94,.88);border-color:rgba(244,63,94,.95);color:#fff;}
        .mbtn--ghost{background:transparent;}
        .mbtn.is-active{outline:2px solid rgba(59,130,246,.45);}

        /* green button */
        .mbtn--success{background:rgba(34,197,94,.90); border-color:rgba(34,197,94,.95); color:#fff;}
        .mbtn--success:hover{background:rgba(34,197,94,.98);}

        .mtabs{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:12px;}
        .mrow{display:flex; gap:10px; flex-wrap:wrap; align-items:center;}
        .mhelp{font-size:12px; line-height:1.6; opacity:.9;}
        .mmono{padding:4px 8px;border-radius:10px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.10);
            font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;}

        .previewBox{border:1px solid rgba(255,255,255,.10); background:rgba(0,0,0,.10); border-radius:14px; overflow:hidden;}
        .previewHead{display:flex; justify-content:space-between; align-items:center; padding:10px 12px; border-bottom:1px solid rgba(255,255,255,.08);}
        .previewBody{padding:12px; min-height:180px;}
        .previewBody .emailFrame{background:#fff; color:#111; border-radius:12px; padding:14px;}
        .previewBody .emailFrame.dark{background:#0b1220; color:#e5e7eb;}

        /* disabled look */
        .is-disabled{
            opacity:.55;
            pointer-events:none;
            filter:grayscale(.2);
        }

        /* ===== modal preview ===== */
        .mailModal{display:none; position:fixed; inset:0; z-index:9999;}
        .mailModal__back{position:absolute; inset:0; background:rgba(0,0,0,.55);}
        .mailModal__box{
            position:relative;
            width:800px; max-width:calc(100vw - 24px);
            height:600px; max-height:calc(100vh - 24px);
            margin:12px auto;
            top:50%; transform:translateY(-50%);
            border:1px solid rgba(255,255,255,.12);
            background:rgba(20,20,20,.94);
            border-radius:16px;
            overflow:hidden;
            box-shadow:0 18px 60px rgba(0,0,0,.45);
        }
        .mailModal__head{
            display:flex; justify-content:space-between; align-items:center;
            padding:10px 12px;
            border-bottom:1px solid rgba(255,255,255,.10);
        }
        .mailModal__body{
            height:calc(100% - 52px);
            overflow:auto;
            padding:12px;
        }
        .mailModal__frameWrap{
            background:#fff;
            border-radius:12px;
            overflow:hidden;
        }
        .mailModal__frame{
            width:100%;
            height:1100px;
            border:0;
            display:block;
            background:#fff;
        }
        .mailModal__hint{font-size:12px; opacity:.75; margin-top:10px}
    </style>
@endpush

@section('content')
    <form method="POST" action="{{ route('admin.mail.send.store') }}" enctype="multipart/form-data" id="mailSendForm">
        @csrf

        {{-- mode --}}
        <input type="hidden" name="send_mode" id="sendMode" value="one">

        {{-- ✅ 여러건 파싱 결과(JSON) 서버 전달용 --}}
        <input type="hidden" name="many_rows_json" id="manyRowsJson" value="[]">

        {{-- ✅ 토큰 시작 컬럼: 2열부터 {_text_02_} (서버에서 참고용) --}}
        <input type="hidden" name="token_base" value="2">

        {{-- ✅ subject/body 미러 (백엔드 키 불일치 대비) --}}
        <input type="hidden" name="subject_tpl" id="subjectTplMirror" value="">
        <input type="hidden" name="body_tpl" id="bodyTplMirror" value="">
        <input type="hidden" name="mail_subject" id="mailSubjectMirror" value="">
        <input type="hidden" name="mail_body" id="mailBodyMirror" value="">

        <div class="a-card" style="padding:16px; margin-bottom:16px;">
            <div class="mtop">
                <div style="min-width:260px;">
                    <div class="a-muted" style="margin-bottom:6px;">발신자</div>
                    <div class="mrow">
                        <input class="a-input" name="from_email" value="{{ config('mail.from.address') }}" readonly style="width:240px;">
                        <input class="a-input" name="from_name" value="{{ config('mail.from.name') }}" readonly style="width:180px;">
                    </div>
                </div>

                <div class="mtop__grow" style="min-width:320px;">
                    <div class="mrow">
                        <label class="a-pill"><input type="radio" name="schedule_type" value="now" checked> 즉시발송</label>
                        <label class="a-pill"><input type="radio" name="schedule_type" value="schedule"> 예약발송</label>

                        {{-- ✅ 직접 입력 불가(실수 방지): readonly + 선택 버튼 --}}
                        <input class="a-input"
                               type="text"
                               name="scheduled_at"
                               id="scheduledAt"
                               placeholder="YYYY-MM-DD HH:mm"
                               style="width:180px"
                               disabled
                               readonly>

                        <button type="button" class="mbtn" id="openSchedulePicker" disabled>선택</button>
                    </div>
                </div>

                <div class="mtop__right">
                    <span class="mmono" id="toCountBadge">0명</span>
                    <span class="mmono" id="approxBadge">예상: 0자</span>
                </div>
            </div>
        </div>

        <div class="mwrap">
            {{-- LEFT: recipients --}}
            <div class="a-card" style="padding:16px;">
                <div class="mtabs">
                    <button type="button" class="mbtn is-active" data-tab="one">단건</button>
                    <button type="button" class="mbtn" data-tab="many">여러건</button>
                    <button type="button" class="mbtn" data-tab="template">템플릿(CSV)</button>
                </div>

                <section data-panel="one">
                    <div class="a-muted" style="margin-bottom:6px;">수신자 (1명)</div>
                    <div class="mrow">
                        <input class="a-input" name="to_email" id="toEmail" placeholder="user@example.com" style="width:260px;">
                        <input class="a-input" name="to_name" id="toName" placeholder="수신자명(선택)" style="width:180px;">
                    </div>
                </section>

                <section data-panel="many" style="display:none;">
                    <div class="a-muted" style="margin-bottom:6px;">수신자 (여러명)</div>

                    <textarea class="a-input" name="to_emails_text" id="toEmailsText" rows="9"
                              placeholder="✅ 1줄 = 1명 (줄바꿈 기준)&#10;✅ 1열: 이메일, 2열~: 토큰(콤마로 구분)&#10;예) sungro81@gmail.com, 이상도, 10000, 쿠폰"></textarea>

                    <div class="a-muted mhelp" style="margin-top:8px;">
                        - 예: <span class="mmono">sungro81@gmail.com, 홍길동, 10000, 쿠폰</span><br>
                        - 매칭(공통 규칙): <span class="mmono">{_text_02_}</span> → 2열(홍길동), <span class="mmono">{_text_03_}</span> → 3열(10000), <span class="mmono">{_text_04_}</span> → 4열(쿠폰)
                    </div>

                    <div class="mrow" style="margin-top:10px;">
                        <button type="button" class="mbtn mbtn--ghost" id="clearMany">비우기</button>
                        <span class="a-muted" id="manyStats" style="font-size:12px;"></span>
                    </div>

                    {{-- ✅ 파싱 미리보기 --}}
                    <div class="previewBox" style="margin-top:10px;">
                        <div class="previewHead">
                            <div class="a-muted">파싱 미리보기 (상위 5줄)</div>
                            <div class="a-muted" style="font-size:12px;">줄바꿈=행 / 콤마=열</div>
                        </div>
                        <div class="previewBody" style="min-height:auto;">
                            <div id="manyPreview" class="a-muted" style="font-size:12px; white-space:pre-wrap;">(입력 전)</div>
                        </div>
                    </div>
                </section>

                <section data-panel="template" style="display:none;">
                    <div class="a-muted" style="margin-bottom:6px;">템플릿 CSV (개인화 발송)</div>

                    <label class="mbtn">
                        CSV 업로드
                        <input type="file" name="to_emails_csv" id="csvFile" accept=".csv,.txt" style="display:none;">
                    </label>

                    <div class="previewBox" style="margin-top:10px;">
                        <div class="previewHead">
                            <div class="a-muted">CSV 미리보기 (첫 5줄)</div>
                            <div class="a-muted" style="font-size:12px;">업로드 실수 방지</div>
                        </div>
                        <div class="previewBody" style="min-height:auto;">
                            <div id="csvPreviewTpl" class="a-muted" style="font-size:12px; white-space:pre-wrap;">(업로드 전)</div>
                        </div>
                    </div>

                    <div class="a-muted mhelp" style="margin-top:10px;">
                        <b>- CSV 파일 작성 요령</b><br>
                        <div class="mmono" style="display:inline-block; margin-top:6px; white-space:pre;">
                            sungro1@naver.com,홍길동,10000,쿠폰
                            sungro1@google.com,이순신,20000,상품권
                            sungro1@nate.com,김개똥,30000,쿠폰
                        </div>
                        <div style="margin-top:8px;">
                            1열은 <b>수신자 이메일</b>, 2열부터는 메시지에 끼워 넣을 <b>사용자 데이터</b>를 콤마로 구분해 작성합니다.
                        </div>

                        <div style="margin-top:10px;"><b>- 발송 문구와 매칭 (공통)</b></div>
                        <div class="mmono" style="display:inline-block; margin-top:6px; white-space:pre;">
                            안녕하세요 {_text_02_} 고객님
                            결제 금액은 {_text_03_}원 입니다.
                            상품 유형: {_text_04_}
                        </div>

                        <div style="margin-top:8px;">
                            <span class="mmono">{_text_02_}</span> → 2열(홍길동)<br>
                            <span class="mmono">{_text_03_}</span> → 3열(10000)<br>
                            <span class="mmono">{_text_04_}</span> → 4열(쿠폰)
                        </div>

                        <div style="margin-top:10px;">
                            사용자 문구는 <span class="mmono">{_text_02_}</span>부터 최대 8개까지 사용 가능:
                            <span class="mmono">{_text_02_} ~ {_text_09_}</span>
                        </div>
                    </div>
                </section>
            </div>

            {{-- RIGHT: message/template/preview --}}
            <div class="a-card" style="padding:16px;">
                <div class="mrow" style="justify-content:space-between; align-items:end;">
                    <div style="flex:1; min-width:260px;">
                        <div class="a-muted" style="margin-bottom:6px;">스킨 (템플릿을 선택하면 스킨은 템플릿에 고정됩니다)</div>
                        <select class="a-input" name="skin_key" id="skinKey" style="max-width:260px;">
                            @foreach(($skins ?? []) as $sk)
                                <option value="{{ data_get($sk,'key') }}">{{ data_get($sk,'label') }}</option>
                            @endforeach
                        </select>
                        <div class="a-muted" id="skinTplHint" style="font-size:12px; margin-top:6px; opacity:.8; display:none;">
                            템플릿이 선택되어 스킨 변경이 잠겨있습니다. (템플릿 선택 해제하면 다시 변경 가능)
                        </div>
                    </div>

                    <div style="display:flex; gap:8px; align-items:center; flex-wrap:nowrap; min-width:0;">
                        {{-- ✅ 템플릿 선택값 서버로도 보내기 (선택사항이지만 디버깅/로그에 도움됨) --}}
                        <select class="a-input" id="tplSelect" name="template_id" style="width:240px; max-width:240px; flex:0 0 240px;">
                            <option value="">템플릿 선택</option>
                            @foreach(($templates ?? []) as $t)
                                <option value="{{ data_get($t,'id') }}">
                                    {{ data_get($t,'title') }} ({{ data_get($t,'code') }})
                                </option>
                            @endforeach
                        </select>
                        <button type="button" class="mbtn" id="tplApply" style="white-space:nowrap;">적용</button>
                    </div>
                </div>

                <div style="height:12px;"></div>

                <div class="mrow">
                    <div style="flex:1;">
                        <div class="a-muted" style="margin-bottom:6px;">제목</div>
                        <input class="a-input" name="subject" id="subjectText" value="{{ old('subject') }}" placeholder="메일 제목을 입력하세요">
                    </div>
                </div>

                <div style="height:10px;"></div>

                <div>
                    <div class="a-muted" style="margin-bottom:6px;">내용</div>
                    <textarea class="a-input" name="body" id="bodyText" rows="10" placeholder="메일 내용을 입력하세요">{{ old('body') }}</textarea>

                    <div class="mrow" style="margin-top:10px; justify-content:space-between;">
                        <div class="mrow">
                            <button type="button" class="mbtn mbtn--ghost" data-insert="{_text_02_}">{_text_02_}</button>
                            <button type="button" class="mbtn mbtn--ghost" data-insert="{_text_03_}">{_text_03_}</button>
                            <button type="button" class="mbtn mbtn--ghost" data-insert="{_text_04_}">{_text_04_}</button>
                            <span class="a-muted" style="font-size:12px;">토큰 빠른 삽입</span>
                        </div>

                        <div class="mrow">
                            <button type="button" class="mbtn mbtn--success" id="openMailPreview">전체 미리보기</button>
                            <button type="submit" class="mbtn mbtn--primary" id="sendBtn">발송</button>
                            <a class="mbtn" href="{{ route('admin.mail.logs') }}">발송 이력</a>
                        </div>
                    </div>
                </div>

                <div style="height:14px;"></div>

                <div class="previewBox">
                    <div class="previewHead">
                        <div class="a-muted">미리보기</div>
                        <div class="a-muted" style="font-size:12px;">선택한 스킨 + 현재 입력값(간이)</div>
                    </div>
                    <div class="previewBody">
                        <div id="previewFrame" class="emailFrame">
                            <div style="font-weight:900; font-size:16px; margin-bottom:10px;" id="pvSubject">(제목)</div>
                            <div id="pvBody" style="line-height:1.6; white-space:normal;"></div>
                        </div>
                    </div>
                </div>

                <script>
                    window.__MAIL_TEMPLATES__ = @json($templates ?? []);
                    window.__MAIL_SKINS__ = @json($skins ?? []);
                </script>
            </div>
        </div>
    </form>

    {{-- ===== 예약발송 날짜/시간/분(5분단위) 선택 모달 ===== --}}
    <div id="schedulePickerModal" class="mailModal" aria-hidden="true">
        <div id="schedulePickerBackdrop" class="mailModal__back"></div>

        <div class="mailModal__box" role="dialog" aria-modal="true" aria-label="예약 발송 시간 선택"
             style="width:520px; height:auto; max-height:calc(100vh - 24px);">
            <div class="mailModal__head">
                <div>
                    <div style="font-weight:900;">예약 발송 시간 선택</div>
                    <div class="mailModal__hint">날짜 · 시간 · 분(5분 단위)</div>
                </div>
                <div style="display:flex; gap:8px; align-items:center;">
                    <button type="button" class="mbtn" id="closeSchedulePicker">닫기</button>
                </div>
            </div>

            <div class="mailModal__body" style="height:auto;">
                <div class="a-card" style="padding:14px;">
                    <div class="mrow" style="align-items:end;">
                        <div>
                            <div class="a-muted" style="margin-bottom:6px;">날짜</div>
                            <input class="a-input" type="date" id="schDate" style="width:180px;">
                        </div>

                        <div>
                            <div class="a-muted" style="margin-bottom:6px;">시간</div>
                            <select class="a-input" id="schHour" style="width:120px;"></select>
                        </div>

                        <div>
                            <div class="a-muted" style="margin-bottom:6px;">분</div>
                            <select class="a-input" id="schMin" style="width:120px;"></select>
                        </div>
                    </div>

                    <div class="mrow" style="margin-top:12px; justify-content:flex-end;">
                        <button type="button" class="mbtn mbtn--ghost" id="resetSchedulePicker">초기화</button>
                        <button type="button" class="mbtn mbtn--primary" id="applySchedulePicker">적용</button>
                    </div>
                </div>
            </div>
        </div>
    </div>

    {{-- ===== 전체 메일 형태 미리보기: 레이어 팝업 + 내부 스크롤 + iframe ===== --}}
    <div id="mailPreviewModal" class="mailModal" aria-hidden="true">
        <div id="mailPreviewBackdrop" class="mailModal__back"></div>

        <div class="mailModal__box" role="dialog" aria-modal="true" aria-label="전체 메일 미리보기">
            <div class="mailModal__head">
                <div>
                    <div style="font-weight:900;">전체 메일 형태 미리보기</div>
                    <div class="mailModal__hint">Esc로 닫기 · 필요하면 “새로고침”</div>
                </div>
                <div style="display:flex; gap:8px; align-items:center;">
                    <button type="button" class="mbtn mbtn--ghost" id="refreshMailPreview">새로고침</button>
                    <button type="button" class="mbtn" id="closeMailPreview">닫기</button>
                </div>
            </div>

            <div class="mailModal__body">
                <div class="mailModal__frameWrap">
                    <iframe id="mailPreviewFrame" class="mailModal__frame"></iframe>
                </div>
            </div>
        </div>
    </div>

    @push('scripts')
        @php
            $previewUrl = \Illuminate\Support\Facades\Route::has('admin.mail.preview')
                ? route('admin.mail.preview')
                : '';
        @endphp

        <script>
            (() => {
                const form = document.getElementById('mailSendForm');
                if (!form) return;

                // ===== elements
                const tabBtns = Array.from(form.querySelectorAll('[data-tab]'));
                const panels  = Array.from(form.querySelectorAll('[data-panel]'));
                const sendModeEl = document.getElementById('sendMode');

                const scheduledAtEl = document.getElementById('scheduledAt');
                const openSchBtn    = document.getElementById('openSchedulePicker'); // ✅ 추가

                const toEmailOneEl    = document.getElementById('toEmail');
                const toEmailsTextEl  = document.getElementById('toEmailsText');

                const manyPreviewEl   = document.getElementById('manyPreview');
                const manyRowsJsonEl  = document.getElementById('manyRowsJson');

                // template csv file
                const csvFileEl = document.getElementById('csvFile');

                const toCountBadgeEl = document.getElementById('toCountBadge');
                const approxBadgeEl  = document.getElementById('approxBadge');

                const skinEl    = document.getElementById('skinKey');
                const skinTplHintEl = document.getElementById('skinTplHint');

                const subjectEl = document.getElementById('subjectText');
                const bodyEl    = document.getElementById('bodyText');

                // subject/body mirrors
                const subjectTplMirrorEl = document.getElementById('subjectTplMirror');
                const bodyTplMirrorEl    = document.getElementById('bodyTplMirror');
                const mailSubjectMirrorEl= document.getElementById('mailSubjectMirror');
                const mailBodyMirrorEl   = document.getElementById('mailBodyMirror');

                // inline preview box
                const pvSubjectEl = document.getElementById('pvSubject');
                const pvBodyEl    = document.getElementById('pvBody');
                const inlineFrameEl = document.getElementById('previewFrame');

                // modal preview
                const modalEl  = document.getElementById('mailPreviewModal');
                const backEl   = document.getElementById('mailPreviewBackdrop');
                const openBtn  = document.getElementById('openMailPreview');
                const closeBtn = document.getElementById('closeMailPreview');
                const refreshBtn = document.getElementById('refreshMailPreview');
                const previewIframeEl = document.getElementById('mailPreviewFrame');

                // templates
                const templates = window.__MAIL_TEMPLATES__ || [];
                const tplSelect = document.getElementById('tplSelect');
                const tplApplyBtn = document.getElementById('tplApply');

                // ✅ 예약발송 Picker modal elements (추가)
                const schModalEl  = document.getElementById('schedulePickerModal');
                const schBackEl   = document.getElementById('schedulePickerBackdrop');
                const schCloseBtn = document.getElementById('closeSchedulePicker');

                const schDateEl   = document.getElementById('schDate');
                const schHourEl   = document.getElementById('schHour');
                const schMinEl    = document.getElementById('schMin');

                const schApplyBtn = document.getElementById('applySchedulePicker');
                const schResetBtn = document.getElementById('resetSchedulePicker');

                // ===== helpers
                function escapeHtml(s){
                    return String(s).replace(/[&<>"']/g, m => ({
                        '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'
                    }[m]));
                }

                function pad2(n){ return String(n).padStart(2,'0'); }
                function formatScheduledAt(dateStr, hh, mm){
                    return `${dateStr} ${pad2(hh)}:${pad2(mm)}`;
                }
                function parseScheduledAt(v){
                    const s = String(v || '').trim();
                    const m = s.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}):(\d{2})$/);
                    if(!m) return null;
                    return { date: m[1], hh: Number(m[2]), mm: Number(m[3]) };
                }
                function next5min(){
                    const d = new Date();
                    d.setSeconds(0,0);
                    const m = d.getMinutes();
                    const add = (5 - (m % 5)) % 5;
                    d.setMinutes(m + add);
                    return d;
                }
                function toDateInputValue(d){
                    const y = d.getFullYear();
                    const m = pad2(d.getMonth()+1);
                    const day = pad2(d.getDate());
                    return `${y}-${m}-${day}`;
                }

                // ✅ scheduled_at 직접 입력 차단(실수 방지)
                if (scheduledAtEl){
                    scheduledAtEl.readOnly = true;
                    scheduledAtEl.addEventListener('keydown', (e) => {
                        if (e.key === 'Tab') return;
                        e.preventDefault();
                    });
                    scheduledAtEl.addEventListener('paste', (e) => e.preventDefault());
                    scheduledAtEl.addEventListener('drop',  (e) => e.preventDefault());
                }

                // ✅ subject/body mirror sync (백엔드 키 불일치 대비)
                function syncMirrors(){
                    const s = subjectEl?.value ?? '';
                    const b = bodyEl?.value ?? '';
                    if(subjectTplMirrorEl) subjectTplMirrorEl.value = s;
                    if(mailSubjectMirrorEl) mailSubjectMirrorEl.value = s;
                    if(bodyTplMirrorEl) bodyTplMirrorEl.value = b;
                    if(mailBodyMirrorEl) mailBodyMirrorEl.value = b;
                }

                // ✅ 템플릿/스킨 동시 선택 금지 상태관리
                function setTemplateLock(isTemplateChosen){
                    if (!skinEl) return;

                    if (isTemplateChosen){
                        skinEl.classList.add('is-disabled');     // CSS로 클릭 막기(pointer-events: none)
                        skinEl.setAttribute('aria-disabled','true');

                        // 키보드 포커스도 막기(선택사항)
                        if (!skinEl.dataset.prevTabIndex) {
                            skinEl.dataset.prevTabIndex = skinEl.getAttribute('tabindex') ?? '';
                        }
                        skinEl.setAttribute('tabindex', '-1');

                        if (skinTplHintEl) skinTplHintEl.style.display = '';
                    } else {
                        skinEl.classList.remove('is-disabled');
                        skinEl.removeAttribute('aria-disabled');

                        // 포커스 복구
                        const prev = skinEl.dataset.prevTabIndex ?? '';
                        if (prev === '') skinEl.removeAttribute('tabindex');
                        else skinEl.setAttribute('tabindex', prev);
                        delete skinEl.dataset.prevTabIndex;

                        if (skinTplHintEl) skinTplHintEl.style.display = 'none';
                    }
                }
                function isTemplateSelected(){
                    return !!(tplSelect && String(tplSelect.value || '').trim());
                }

                // ===== tabs
                function setTab(tab){
                    tabBtns.forEach(b => b.classList.toggle('is-active', b.dataset.tab === tab));
                    panels.forEach(p => p.style.display = (p.dataset.panel === tab) ? '' : 'none');

                    // 기존 규칙 유지: template 탭은 send_mode=csv
                    const sendVal = (tab === 'template') ? 'csv' : tab;
                    sendModeEl.value = sendVal;

                    refreshCounts();
                }
                tabBtns.forEach(b => b.addEventListener('click', () => setTab(b.dataset.tab)));

                // ===== schedule (기존 로직 유지 + 버튼 토글만 추가)
                form.querySelectorAll('input[name="schedule_type"]').forEach(r => {
                    r.addEventListener('change', () => {
                        const isSch = form.querySelector('input[name="schedule_type"][value="schedule"]')?.checked;
                        if (!scheduledAtEl) return;

                        scheduledAtEl.disabled = !isSch;

                        // ✅ 추가: 선택 버튼도 같이 토글
                        if (openSchBtn) openSchBtn.disabled = !isSch;

                        if (!isSch) scheduledAtEl.value = '';
                    });
                });

                // ✅ 여러건 파싱: "줄바꿈=행", "콤마=열"
                // 규칙: 1열=email, 2열부터 {_text_02_} 시작
                function parseManyLines(rawText){
                    const text = String(rawText || '');
                    const lines = text
                        .replace(/\r\n/g, '\n')
                        .replace(/\r/g, '\n')
                        .split('\n')
                        .map(s => s.trim())
                        .filter(Boolean);

                    const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

                    const rows = [];
                    const errors = [];
                    const seen = new Set();

                    for (let i = 0; i < lines.length; i++) {
                        const lineNo = i + 1;
                        const line = lines[i];

                        // 한 줄에서만 콤마로 분리 (빈 값 제거)
                        const cols = line.split(',').map(s => s.trim()).filter(s => s !== '');
                        const email = String(cols[0] || '').toLowerCase();

                        if (!email || !emailRe.test(email)) {
                            errors.push({ line: lineNo, reason: 'invalid_email', raw: line });
                            continue;
                        }

                        if (seen.has(email)) {
                            errors.push({ line: lineNo, reason: 'duplicate_email', raw: line });
                            continue;
                        }
                        seen.add(email);

                        const tokenCols = cols.slice(1); // 2열~ 실제 토큰값들

                        // ✅ 핵심: tokens[0]은 더미로 비워두고
                        // tokens[1]이 2열(= {_text_02_})이 되게 맞춤
                        const tokens = [''].concat(tokenCols);

                        // ✅ 안전용: placeholder 그대로 key map도 함께 전송
                        const token_map = {};
                        for (let j = 0; j < tokenCols.length && j < 8; j++) {
                            const n = j + 2; // 2..9
                            const pad = String(n).padStart(2,'0');
                            const key = `{_text_${pad}_}`; // 예: {_text_02_}
                            token_map[key] = tokenCols[j];
                        }

                        rows.push({
                            email,
                            tokens,       // ['', col2, col3...]
                            token_map,    // {'{_text_02_}': col2, ...}
                            line: lineNo,
                            raw: line,
                        });
                    }

                    return {
                        totalLines: lines.length,
                        validLines: rows.length,
                        errors,
                        rows,
                    };
                }

                // ===== counts
                function refreshCounts(){
                    const mode = sendModeEl.value;
                    let cnt = 0;

                    if(mode === 'one'){
                        const e = (toEmailOneEl?.value||'').trim();
                        cnt = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e) ? 1 : 0;

                    } else if(mode === 'many'){
                        const r = parseManyLines(toEmailsTextEl?.value || '');
                        cnt = r.rows.length;

                        // 서버 전달용 JSON 갱신
                        if (manyRowsJsonEl) {
                            manyRowsJsonEl.value = JSON.stringify(r.rows);
                        }

                        const manyStats = document.getElementById('manyStats');
                        if(manyStats) {
                            const errN = r.errors.length;
                            manyStats.textContent = `입력 ${r.totalLines}줄 / 유효 ${r.rows.length}명 / 오류 ${errN}건`;
                        }

                        // 미리보기(상위 5개)
                        if (manyPreviewEl){
                            if (!r.totalLines) {
                                manyPreviewEl.textContent = '(입력 전)';
                            } else if (!r.rows.length && r.errors.length) {
                                const first = r.errors.slice(0, 5)
                                    .map(e => `라인 ${e.line}: 이메일 형식 오류/중복 → ${e.raw}`)
                                    .join('\n');
                                manyPreviewEl.textContent = first;
                            } else {
                                const top = r.rows.slice(0,5).map(x => {
                                    // 표시: email | col2 | col3 ...
                                    const shown = x.tokens.slice(1); // 더미 제외
                                    const tok = shown.length ? (' | ' + shown.join(' | ')) : '';
                                    return `${x.email}${tok}`;
                                }).join('\n');

                                // 매핑 예시 한 줄만 보여주기
                                const sample = r.rows[0];
                                let mapHint = '';
                                if (sample && sample.token_map){
                                    const m2 = sample.token_map['{_text_02_}'] ?? '';
                                    const m3 = sample.token_map['{_text_03_}'] ?? '';
                                    const m4 = sample.token_map['{_text_04_}'] ?? '';
                                    mapHint = `\n\n매핑 예시:\n{_text_02_}=${m2}\n{_text_03_}=${m3}\n{_text_04_}=${m4}`;
                                }

                                const errHint = r.errors.length
                                    ? `\n\n(오류 ${r.errors.length}건: 예) ` + r.errors.slice(0,2).map(e => `라인 ${e.line}`).join(', ')
                                    : '';

                                manyPreviewEl.textContent = top + mapHint + errHint;
                            }
                        }

                    } else if (mode === 'template' || mode === 'csv') {
                        cnt = ((csvFileEl?.files||[]).length > 0) ? 1 : 0;

                    } else {
                        cnt = 0;
                    }

                    if (toCountBadgeEl){
                        toCountBadgeEl.textContent = (mode === 'template' || mode === 'csv') ? 'CSV' : (cnt + '명');
                    }

                    const approx = ((subjectEl?.value||'') + (bodyEl?.value||'')).length;
                    if (approxBadgeEl) approxBadgeEl.textContent = `예상: ${approx}자`;
                }

                [toEmailsTextEl, toEmailOneEl].forEach(el => el?.addEventListener('input', refreshCounts));
                refreshCounts();

                // clear many
                document.getElementById('clearMany')?.addEventListener('click', () => {
                    if(toEmailsTextEl) toEmailsTextEl.value='';
                    refreshCounts();
                });

                // ===== token insert
                form.querySelectorAll('[data-insert]').forEach(btn => {
                    btn.addEventListener('click', () => {
                        const token = btn.getAttribute('data-insert') || '';
                        if(!bodyEl) return;
                        const start = bodyEl.selectionStart ?? bodyEl.value.length;
                        const end   = bodyEl.selectionEnd ?? bodyEl.value.length;
                        bodyEl.value = bodyEl.value.slice(0,start) + token + bodyEl.value.slice(end);
                        bodyEl.focus();
                        bodyEl.selectionStart = bodyEl.selectionEnd = start + token.length;

                        syncMirrors();
                        refreshCounts();
                        refreshInlinePreview();
                        debouncedIframePreview();
                    });
                });

                // ===== CSV preview (first 5 lines)
                function previewCsv(file, targetEl){
                    if(!file || !targetEl) return;
                    const reader = new FileReader();
                    reader.onload = () => {
                        const txt = String(reader.result || '');
                        const lines = txt.split(/\r\n|\n/).slice(0, 5);
                        targetEl.textContent = lines.length ? lines.join('\n') : '(비어있음)';
                    };
                    reader.onerror = () => targetEl.textContent = '(읽기 실패)';
                    reader.readAsText(file);
                }

                csvFileEl?.addEventListener('change', (e) => {
                    previewCsv(e.target.files?.[0], document.getElementById('csvPreviewTpl'));
                    refreshCounts();
                });

                // ===== template apply
                tplApplyBtn?.addEventListener('click', () => {
                    const id = Number(tplSelect?.value || 0);
                    if(!id){ alert('템플릿을 선택하세요.'); return; }

                    const t = templates.find(x => Number(x.id) === id);
                    if(!t){ alert('템플릿을 찾을 수 없습니다.'); return; }

                    if(subjectEl) subjectEl.value = String(t.subject_tpl ?? t.title ?? '');
                    if(bodyEl)    bodyEl.value    = String(t.body_tpl ?? '');

                    // 템플릿이 가진 스킨 적용 + 스킨 잠금
                    if(skinEl && (t.skin_key ?? t.skinKey)) skinEl.value = String(t.skin_key ?? t.skinKey);

                    // 템플릿 선택 상태면 스킨 변경 금지
                    setTemplateLock(true);

                    syncMirrors();
                    refreshCounts();
                    refreshInlinePreview();
                    debouncedIframePreview();
                });

                // 템플릿 선택/해제에 따라 스킨 lock/unlock
                tplSelect?.addEventListener('change', () => {
                    const chosen = isTemplateSelected();
                    setTemplateLock(chosen);
                });

                // 스킨을 바꾸려고 하면, 템플릿이 선택된 상태라면 템플릿을 해제하고 스킨 모드로 전환
                skinEl?.addEventListener('change', () => {
                    if (isTemplateSelected()){
                        if (tplSelect) tplSelect.value = '';
                        setTemplateLock(false);
                    }
                    syncMirrors();
                    refreshInlinePreview();
                    debouncedIframePreview();
                });

                // ===== inline preview box
                function refreshInlinePreview(){
                    const sub  = (subjectEl?.value || '').trim() || '(제목)';
                    const body = (bodyEl?.value || '').trim();

                    if(pvSubjectEl) pvSubjectEl.textContent = sub;
                    if(pvBodyEl) pvBodyEl.innerHTML = body
                        ? escapeHtml(body).replace(/\n/g,'<br>')
                        : '<span style="opacity:.7;">(내용)</span>';

                    const k = (skinEl?.value || 'clean');
                    if(inlineFrameEl){
                        inlineFrameEl.classList.toggle('dark', k === 'dark');
                    }
                }

                subjectEl?.addEventListener('input', () => { syncMirrors(); refreshCounts(); refreshInlinePreview(); debouncedIframePreview(); });
                bodyEl?.addEventListener('input', () => { syncMirrors(); refreshCounts(); refreshInlinePreview(); debouncedIframePreview(); });

                // 초기 동기화
                syncMirrors();
                refreshInlinePreview();

                // 초기 lock 상태 반영
                setTemplateLock(isTemplateSelected());

                // ===== iframe preview (server render) - only when modal open
                const PREVIEW_URL = @json($previewUrl);
                const csrf = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');

                let isModalOpen = false;
                let previewDirty = true;

                async function updateIframePreview(){
                    if(!previewIframeEl) return;

                    if(!PREVIEW_URL){
                        previewIframeEl.srcdoc = `<div style="padding:16px;font-family:system-ui;">
                            <b>미리보기 라우트가 없습니다.</b><br>
                            <span style="opacity:.7;">Route::post('admin.mail.preview') 를 추가해 주세요.</span>
                        </div>`;
                        previewDirty = false;
                        return;
                    }

                    try {
                        const fd = new FormData();
                        fd.append('skin_key', skinEl?.value || '');
                        fd.append('subject',  subjectEl?.value || '');
                        fd.append('body',     bodyEl?.value || '');

                        const res = await fetch(PREVIEW_URL, {
                            method: 'POST',
                            headers: csrf ? { 'X-CSRF-TOKEN': csrf } : {},
                            body: fd,
                        });

                        const html = await res.text();
                        previewIframeEl.srcdoc = html;
                        previewDirty = false;
                    } catch (e) {
                        previewIframeEl.srcdoc = `<div style="padding:16px;font-family:system-ui;">
                            <b>미리보기 실패</b><br><span style="opacity:.7;">${escapeHtml(String(e))}</span>
                        </div>`;
                        previewDirty = false;
                    }
                }

                let iframeTimer = null;
                function debouncedIframePreview(){
                    previewDirty = true;
                    if(!isModalOpen) return;
                    clearTimeout(iframeTimer);
                    iframeTimer = setTimeout(updateIframePreview, 200);
                }

                // ===== modal open/close (메일 미리보기)
                function openModal(){
                    if(!modalEl) return;
                    modalEl.style.display = 'block';
                    document.documentElement.style.overflow = 'hidden';
                    isModalOpen = true;
                    if(previewDirty) updateIframePreview();
                }
                function closeModal(){
                    if(!modalEl) return;
                    modalEl.style.display = 'none';
                    document.documentElement.style.overflow = '';
                    isModalOpen = false;
                }

                openBtn?.addEventListener('click', openModal);
                closeBtn?.addEventListener('click', closeModal);
                backEl?.addEventListener('click', closeModal);
                refreshBtn?.addEventListener('click', () => { previewDirty = true; updateIframePreview(); });

                // ===== 예약발송 Picker modal (추가)
                function buildSelectOptions(){
                    if (schHourEl && schHourEl.options.length === 0){
                        for(let h=0; h<24; h++){
                            const opt = document.createElement('option');
                            opt.value = String(h);
                            opt.textContent = pad2(h);
                            schHourEl.appendChild(opt);
                        }
                    }
                    if (schMinEl && schMinEl.options.length === 0){
                        for(let m=0; m<60; m+=5){
                            const opt = document.createElement('option');
                            opt.value = String(m);
                            opt.textContent = pad2(m);
                            schMinEl.appendChild(opt);
                        }
                    }
                }

                function fillPickerFromCurrent(){
                    buildSelectOptions();

                    const cur = parseScheduledAt(scheduledAtEl?.value || '');
                    if (cur){
                        if (schDateEl) schDateEl.value = cur.date;
                        if (schHourEl) schHourEl.value = String(cur.hh);
                        if (schMinEl)  schMinEl.value  = String(cur.mm - (cur.mm % 5));
                        return;
                    }

                    const d = next5min();
                    if (schDateEl) schDateEl.value = toDateInputValue(d);
                    if (schHourEl) schHourEl.value = String(d.getHours());
                    if (schMinEl)  schMinEl.value  = String(d.getMinutes() - (d.getMinutes()%5));
                }

                let schModalOpen = false;

                function openScheduleModal(){
                    if(!schModalEl) return;
                    fillPickerFromCurrent();
                    schModalEl.style.display = 'block';
                    document.documentElement.style.overflow = 'hidden';
                    schModalOpen = true;
                }

                function closeScheduleModal(){
                    if(!schModalEl) return;
                    schModalEl.style.display = 'none';
                    document.documentElement.style.overflow = '';
                    schModalOpen = false;
                }

                openSchBtn?.addEventListener('click', () => {
                    const isSch = form.querySelector('input[name="schedule_type"][value="schedule"]')?.checked;
                    if (!isSch) return;
                    openScheduleModal();
                });

                schCloseBtn?.addEventListener('click', closeScheduleModal);
                schBackEl?.addEventListener('click', closeScheduleModal);

                schResetBtn?.addEventListener('click', () => {
                    if (scheduledAtEl) scheduledAtEl.value = '';
                    fillPickerFromCurrent();
                    refreshCounts();
                    syncMirrors();
                    refreshInlinePreview();
                    debouncedIframePreview();
                });

                schApplyBtn?.addEventListener('click', () => {
                    const dateStr = (schDateEl?.value || '').trim();
                    const hh = Number(schHourEl?.value || 0);
                    const mm = Number(schMinEl?.value || 0);

                    if (!dateStr){
                        alert('날짜를 선택하세요.');
                        return;
                    }

                    if (scheduledAtEl){
                        scheduledAtEl.value = formatScheduledAt(dateStr, hh, mm); // "YYYY-MM-DD HH:mm"
                    }

                    refreshCounts();
                    syncMirrors();
                    refreshInlinePreview();
                    debouncedIframePreview();

                    closeScheduleModal();
                });

                // Esc 처리: 두 모달 모두 닫기
                document.addEventListener('keydown', (e) => {
                    if(e.key !== 'Escape') return;

                    if (modalEl?.style.display === 'block') closeModal();
                    if (schModalOpen) closeScheduleModal();
                });

                // 초기 schedule 상태 반영(버튼 disable 동기화)
                (() => {
                    const isSch = form.querySelector('input[name="schedule_type"][value="schedule"]')?.checked;
                    if (openSchBtn) openSchBtn.disabled = !isSch;
                })();

                // ===== submit guard
                form.addEventListener('submit', (e) => {
                    // ✅ submit 직전 한번 더 동기화 (주제/내용 누락 방어)
                    syncMirrors();

                    const mode = sendModeEl.value;
                    const sub = (subjectEl?.value||'').trim();
                    const body = (bodyEl?.value||'').trim();

                    if(!sub){ e.preventDefault(); alert('제목을 입력하세요.'); return; }
                    if(!body){ e.preventDefault(); alert('내용을 입력하세요.'); return; }

                    if(mode === 'one'){
                        const em = (toEmailOneEl?.value||'').trim();
                        if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(em)){ e.preventDefault(); alert('수신자 이메일(단건)을 확인하세요.'); return; }
                    }

                    if(mode === 'many'){
                        const r = parseManyLines(toEmailsTextEl?.value || '');
                        if(r.rows.length < 1){
                            e.preventDefault();
                            alert('여러건 수신자를 입력하세요. (1줄=1명, 1열=email, 2열~ 토큰)');
                            return;
                        }

                        // 오류가 있으면 발송 막기
                        if(r.errors.length){
                            e.preventDefault();
                            const lines = r.errors.slice(0, 8).map(x => x.line).join(', ');
                            alert(`여러건 입력에 오류가 있습니다.\n- 오류 ${r.errors.length}건\n- 확인 라인: ${lines}${r.errors.length > 8 ? ' ...' : ''}`);
                            return;
                        }

                        // 서버 전달값 갱신 보장
                        if (manyRowsJsonEl) manyRowsJsonEl.value = JSON.stringify(r.rows);
                    }

                    if(mode === 'template' || mode === 'csv'){
                        const hasT = (csvFileEl?.files||[]).length > 0;
                        if(!hasT){ e.preventDefault(); alert('템플릿 CSV를 업로드하세요.'); return; }
                    }

                    const isSch = form.querySelector('input[name="schedule_type"][value="schedule"]')?.checked;
                    if(isSch && !(scheduledAtEl?.value||'').trim()){ e.preventDefault(); alert('예약 시간을 선택하세요.'); return; }

                    document.getElementById('sendBtn')?.setAttribute('disabled','disabled');
                });

            })();
        </script>
    @endpush
@endsection
