{# 경로/파일: realcom-nas-ui/templates/backup.html #}
{% extends "base.html" %}
{% block title %}백업 관리 - NAS UI{% endblock %}

{% block content %}
<style>
  /* ===== 고객 친화: 크게/선명하게 ===== */
  .bk-wrap { padding:16px; max-width:1100px; }
  .bk-wrap h2 { margin: 0 0 6px 0; }
  .bk-help { color:#666; font-size:14px; line-height:1.65; margin: 0 0 14px 0; }

  /* 버튼 크게 */
  .btn-big, .edit-btn, .del-btn, .btn-sm {
    font-size: 14px !important;
    padding: 10px 14px !important;
    border-radius: 10px !important;
    min-height: 40px;
    cursor: pointer;
  }
  .btn-sm { padding: 9px 12px !important; }

  /* 입력 크게 */
  input[type="text"], input[type="time"], select {
    font-size: 14px;
    height: 40px;
    padding: 8px 10px;
    border-radius: 10px;
  }

  /* 상태 배지 */
  .badge {
    display:inline-block; padding:6px 10px; border-radius:999px;
    font-size:12px; line-height:1; vertical-align:middle;
  }
  .badge-ok { background:#e8f7ee; color:#1f7a3f; border:1px solid #bfe9cd; }
  .badge-warn { background:#fff4e5; color:#8a4b00; border:1px solid #ffd9a6; }
  .badge-bad { background:#fdecec; color:#a12a2a; border:1px solid #f8bcbc; }

  .muted { color:#888; font-size:12px; line-height:1.5; }
  .row-flex { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }

  /* API 오류 메시지 박스 */
  .errbox {
    display:none;
    margin-top:10px;
    background:#fff7f7;
    border:1px solid #f3b2b2;
    color:#9b2b2b;
    padding:10px 12px;
    border-radius:10px;
    font-size:12px;
    white-space: pre-wrap;
  }

  /* 테이블 버튼 영역 */
  .act-wrap { display:flex; gap:8px; flex-wrap:wrap; }

  /* 고객용 큰 액션 버튼 */
  .btn-restore {
    background:#1f6feb; border:1px solid #1f6feb; color:#fff;
  }
  .btn-restore:hover { filter: brightness(0.95); }
  .btn-delete {
    background:#ffefef; border:1px solid #f4b4b4; color:#a12a2a;
  }
</style>

<div class="bk-wrap">

  <h2>백업 관리</h2>
  <p class="bk-help">
    이 화면은 <b>문제가 생겼을 때 이전 상태로 되돌리기</b> 위한 <b>백업</b>을 관리하는 곳입니다.<br>
    평소에는 <b>자동 백업</b>만 켜두면 됩니다.
  </p>

  <!-- 현재 상태 -->
  <div class="panel" style="margin-bottom:16px;">
    <h3>현재 상태</h3>
    <table class="info-table">
      <tr>
        <th>백업 저장 위치</th>
        <td id="bk-path">-</td>
      </tr>
      <tr>
        <th>자동 백업</th>
        <td id="bk-schedule">-</td>
      </tr>
      <tr>
        <th>백업 상태</th>
        <td>
          <span id="bk-badge" class="badge badge-warn">확인중</span>
          <span id="bk-status" style="margin-left:8px;">-</span>
        </td>
      </tr>
    </table>

    <div class="muted" style="margin-top:10px;">
      ※ “자동 백업”을 켜두면, 정해진 시간에 자동으로 백업이 만들어집니다.
    </div>

    <div id="bk-err" class="errbox"></div>
  </div>

  <!-- 지금 백업 만들기 -->
  <div class="panel" style="margin-bottom:16px;">
    <h3>백업</h3>

    <div class="muted" style="margin-bottom:10px; line-height:1.6;">
      이 백업은 <b>서버 프로그램과 설정을 복구하기 위한 백업</b>입니다.<br>
      <b>데이터 전용 하드디스크(공유폴더에 저장된 파일)는 영향을 받지 않습니다.</b><br>
      <span style="color:#a3a3a3;">
        단, 서버 자체에 설치된 프로그램이나 설정,
        시스템 디스크에 저장된 파일은
        백업 시점 상태로 되돌아갈 수 있습니다.
      </span>
    </div>

    <div class="row-flex">
      <button class="edit-btn btn-big" onclick="runSnapshot()">지금 백업 만들기</button>
      <span id="run-status" style="font-size:14px;"></span>
      <button class="btn-sm" onclick="refreshAll(true)" style="margin-left:auto;">새로고침</button>
      <span id="last-updated" class="muted"></span>
    </div>
  </div>

  <!-- 자동 백업 설정 (손님용) -->
  <div class="panel" style="margin-bottom:16px;">
    <h3>자동 백업 켜기</h3>
    <div style="color:#666; font-size:14px; margin-bottom:10px; line-height:1.6;">
      <b>추천</b>: <b>매일 새벽 3시</b> 또는 <b>6시간마다</b><br>
      (컴퓨터를 많이 안 쓰는 시간에 돌아가면 더 좋습니다.)
    </div>

    <div class="row-flex" style="margin-bottom:12px;">
      <select id="quick-type" onchange="onQuickTypeChange()">
        <option value="daily">매일</option>
        <option value="weekly">매주</option>
        <option value="hourly6">6시간마다</option>
        <option value="hourly12">12시간마다</option>
      </select>

      <select id="quick-weekday" style="display:none;">
        <option value="1">월요일</option>
        <option value="2">화요일</option>
        <option value="3">수요일</option>
        <option value="4">목요일</option>
        <option value="5">금요일</option>
        <option value="6">토요일</option>
        <option value="0">일요일</option>
      </select>

      <input id="quick-time" type="time" value="03:00">

      <input id="pol-name" type="text" placeholder="이름 (예: 매일 새벽 3시 자동 백업)" style="width:340px;">

      <button class="edit-btn btn-big" onclick="createPolicyQuick()">자동 백업 추가</button>

      <span id="pol-msg" style="font-size:14px; color:#666;"></span>

      <button class="btn-sm" onclick="toggleAdvanced()" style="margin-left:auto;">관리자용(숨김)</button>
    </div>

    <!-- 고급: 관리자용 -->
    <div id="advanced-box" style="display:none; padding:10px; border:1px dashed #ddd; border-radius:10px; margin-bottom:12px; background:#fafafa;">
      <div style="font-size:13px; color:#666; margin-bottom:8px;">
        관리자용 설정입니다. 일반 손님은 건드릴 필요 없습니다.
      </div>
      <div class="row-flex">
        <input id="pol-schedule" type="text" placeholder="고급 스케줄 입력" style="width:320px;">
        <button class="btn-sm" onclick="fillFromQuick()">위 간편설정 값으로 채우기</button>
        <button class="edit-btn" onclick="createPolicyAdvanced()">고급으로 추가</button>
      </div>
    </div>

    <table id="policy-table">
      <tr>
        <th style="width:140px;">번호</th>
        <th>자동 백업 이름</th>
        <th style="width:220px;">실행 시간</th>
        <th style="width:320px;">관리</th>
      </tr>
    </table>

    <div id="policy-empty" style="display:none; margin-top:10px; color:#888; font-size:14px;">
      자동 백업이 없습니다. 위에서 “자동 백업 추가”를 눌러주세요.
    </div>
  </div>

  <!-- 백업 목록 -->
  <div class="panel">
    <h3>백업 목록</h3>
    <div class="muted" style="margin-bottom:8px;">
      아래는 저장된 백업입니다. <b>복구</b>를 누르면 해당 날짜로 되돌릴 수 있습니다.<br>
      <b>삭제(공간 확보)</b>는 저장 공간이 부족할 때만 하세요.
    </div>

    <table id="backup-table">
      <tr>
        <th>백업 이름</th>
        <th style="width:220px;">만든 날짜</th>
        <th style="width:260px;">관리</th>
      </tr>
    </table>
    <div id="snap-empty" style="display:none; margin-top:10px; color:#888; font-size:14px;">
      백업이 없습니다.
    </div>
  </div>

</div>

<script>
/* =========================
   공통
   ========================= */
function nowStr() {
  const d = new Date();
  const pad = (n)=> String(n).padStart(2,'0');
  return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}

function apiJson(url, opts={}) {
  return fetch(url, Object.assign({ credentials:"same-origin", cache:"no-store" }, opts))
    .then(async (r) => {
      let data = null;
      try { data = await r.json(); } catch(e) {}
      if (!r.ok) {
        const msg = (data && (data.error || data.msg)) ? (data.error || data.msg) : ("HTTP " + r.status);
        throw new Error(msg);
      }
      return data;
    });
}

async function apiJsonRetry(url, opts={}, tries=5, delayMs=600) {
  let lastErr = null;
  for (let i=0; i<tries; i++) {
    try {
      return await apiJson(url, opts);
    } catch(e) {
      lastErr = e;
      await new Promise(res => setTimeout(res, delayMs * (i+1)));
    }
  }
  throw lastErr || new Error("API failed");
}

function setText(id, v) {
  const el = document.getElementById(id);
  if (el) el.innerText = (v === null || v === undefined || v === "") ? "-" : v;
}

function setBadge(kind, text) {
  const b = document.getElementById("bk-badge");
  if (!b) return;
  b.className = "badge " + (kind === "ok" ? "badge-ok" : kind === "bad" ? "badge-bad" : "badge-warn");
  b.innerText = text;
}

function escapeHtml(s) {
  s = (s === null || s === undefined) ? "" : String(s);
  return s
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}
function jsQuote(s) {
  s = (s === null || s === undefined) ? "" : String(s);
  return s.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
}

/* =========================
   상태 조회
   ========================= */
async function loadStatus() {
  const errbox = document.getElementById("bk-err");
  if (errbox) { errbox.style.display="none"; errbox.innerText=""; }

  try {
    const d = await apiJsonRetry("/api/timeshift/status", {}, 5, 500);
    setText("bk-path", d.path || "-");
    setText("bk-schedule", d.schedule || "꺼짐");

    if (d.ok) {
      setBadge("ok", "정상");
      setText("bk-status", "백업 기능 정상");
    } else {
      setBadge("bad", "오류");
      setText("bk-status", "확인 필요");
    }

    const detail = (d.check || "").trim();
    if (!d.ok && detail && errbox) {
      errbox.style.display = "block";
      errbox.innerText = detail.slice(0, 1200);
    }
  } catch(e) {
    setBadge("bad", "오류");
    setText("bk-status", "상태 확인 실패: " + e.message);
    if (errbox) {
      errbox.style.display="block";
      errbox.innerText = "상태 API 호출 실패: " + e.message;
    }
  }
}

/* =========================
   백업 목록 (스냅샷 목록)
   ========================= */
async function loadSnapshots() {
  const table = document.getElementById("backup-table");
  const empty = document.getElementById("snap-empty");
  Array.from(table.querySelectorAll("tr")).slice(1).forEach(tr => tr.remove());

  try {
    const list = await apiJsonRetry("/api/timeshift/list", {}, 6, 600);

    if (!Array.isArray(list) || list.length === 0) {
      empty.style.display = "block";
      empty.innerText = "백업이 없습니다.";
      return;
    }
    empty.style.display = "none";

    list.forEach(b => {
      const tr = document.createElement("tr");
      const name = (b && b.name) ? b.name : "-";
      const date = (b && b.date) ? b.date : "-";

      tr.innerHTML = `
        <td>${escapeHtml(name)}</td>
        <td>${escapeHtml(date)}</td>
        <td>
          <div class="act-wrap">
            <button class="btn-big btn-restore" onclick="restoreSnapshot('${jsQuote(name)}')">복구</button>
            <button class="btn-big btn-delete" onclick="deleteSnapshot('${jsQuote(name)}')">삭제(공간 확보)</button>
          </div>
        </td>
      `;
      table.appendChild(tr);
    });

  } catch(e) {
    empty.style.display = "block";
    empty.innerText = "목록을 불러오지 못했습니다: " + e.message;
  }
}

/* =========================
   지금 백업 만들기
   ========================= */
let _runningCreate = false;
async function runSnapshot() {
  if (_runningCreate) return;
  _runningCreate = true;

  const st = document.getElementById("run-status");
  st.innerText = "백업 만드는 중...";

  try {
    const r = await apiJsonRetry("/api/timeshift/create", { method:"POST" }, 2, 800);
    st.innerText = (r && r.ok) ? "완료" : "실패";
  } catch(e) {
    st.innerText = "오류: " + e.message;
  } finally {
    _runningCreate = false;
    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  }
}

/* =========================
   복구 / 삭제
   ========================= */
let _runningRestore = false;
async function restoreSnapshot(name) {
  if (_runningRestore) return;

  const ok = confirm(
    "복구를 실행하면 컴퓨터가 해당 날짜 상태로 되돌아갑니다.\n" +
    "진행 중에는 시간이 걸릴 수 있습니다.\n\n" +
    "정말 복구하시겠습니까?"
  );
  if (!ok) return;

  _runningRestore = true;
  try {
    await apiJsonRetry("/api/timeshift/restore", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ name })
    }, 2, 1000);

    alert("복구 작업을 시작했습니다.\n진행 상황은 잠시 후 상태를 확인하세요.");
  } catch(e) {
    alert("복구 실패: " + e.message);
  } finally {
    _runningRestore = false;
    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  }
}

async function deleteSnapshot(name) {
  const ok = confirm(
    "이 백업을 삭제하면 되돌릴 수 없습니다.\n" +
    "저장 공간이 부족할 때만 삭제하세요.\n\n" +
    "정말 삭제하시겠습니까?"
  );
  if (!ok) return;

  try {
    await apiJsonRetry("/api/timeshift/delete", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ name })
    }, 2, 800);

    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  } catch(e) {
    alert("삭제 실패: " + e.message);
  }
}

/* =========================
   자동 백업(정책)
   ========================= */
function buildCronFromQuick() {
  const type = (document.getElementById("quick-type").value || "daily");
  const time = (document.getElementById("quick-time").value || "03:00");
  const weekday = (document.getElementById("quick-weekday").value || "1");

  const parts = time.split(":");
  const hh = String(parseInt(parts[0] || "3", 10));
  const mm = String(parseInt(parts[1] || "0", 10));

  if (type === "daily") return `${mm} ${hh} * * *`;
  if (type === "weekly") return `${mm} ${hh} * * ${weekday}`;
  if (type === "hourly6") return `0 */6 * * *`;
  if (type === "hourly12") return `0 */12 * * *`;
  return `${mm} ${hh} * * *`;
}

function buildNameFromQuick() {
  const type = (document.getElementById("quick-type").value || "daily");
  const time = (document.getElementById("quick-time").value || "03:00");
  const weekday = (document.getElementById("quick-weekday").value || "1");
  const wdMap = { "0":"일", "1":"월", "2":"화", "3":"수", "4":"목", "5":"금", "6":"토" };

  if (type === "daily") return `매일 ${time} 자동 백업`;
  if (type === "weekly") return `매주 ${wdMap[weekday] || "월"}요일 ${time} 자동 백업`;
  if (type === "hourly6") return `6시간마다 자동 백업`;
  if (type === "hourly12") return `12시간마다 자동 백업`;
  return `매일 ${time} 자동 백업`;
}

function onQuickTypeChange() {
  const type = document.getElementById("quick-type").value;
  const wd = document.getElementById("quick-weekday");
  const tm = document.getElementById("quick-time");

  wd.style.display = (type === "weekly") ? "" : "none";
  tm.disabled = (type === "hourly6" || type === "hourly12");

  const nameEl = document.getElementById("pol-name");
  if (nameEl && !(nameEl.value || "").trim()) {
    nameEl.value = buildNameFromQuick();
  }
}

function toggleAdvanced() {
  const box = document.getElementById("advanced-box");
  box.style.display = (box.style.display === "none" || box.style.display === "") ? "block" : "none";
}
function fillFromQuick() {
  document.getElementById("pol-schedule").value = buildCronFromQuick();
  const nm = document.getElementById("pol-name");
  if (nm && !(nm.value || "").trim()) nm.value = buildNameFromQuick();
}

async function loadPolicies() {
  const table = document.getElementById("policy-table");
  const empty = document.getElementById("policy-empty");
  Array.from(table.querySelectorAll("tr")).slice(1).forEach(tr => tr.remove());

  try {
    const list = await apiJsonRetry("/api/timeshift/policies", {}, 4, 700);

    if (!Array.isArray(list) || list.length === 0) {
      empty.style.display = "block";
      empty.innerText = "자동 백업이 없습니다. 위에서 “자동 백업 추가”를 눌러주세요.";
      return;
    }
    empty.style.display = "none";

    list.forEach(p => {
      const pid = (p && p.id) ? String(p.id) : "";
      const pname = (p && p.name) ? p.name : "-";
      const sched = (p && p.schedule) ? p.schedule : "";

      const tr = document.createElement("tr");
      tr.innerHTML = `
        <td><code>${escapeHtml(pid || "-")}</code></td>
        <td>${escapeHtml(pname)}</td>
        <td><input type="text" value="${escapeHtml(sched)}" style="width:200px;" id="sched-${escapeHtml(pid)}"></td>
        <td>
          <div class="act-wrap">
            <button class="edit-btn btn-big" onclick="runPolicy('${jsQuote(pid)}')">지금 실행</button>
            <button class="btn-sm" onclick="updatePolicy('${jsQuote(pid)}')">시간 수정</button>
            <button class="del-btn btn-big" onclick="deletePolicy('${jsQuote(pid)}')">자동 백업 끄기</button>
          </div>
        </td>
      `;
      table.appendChild(tr);
    });

  } catch(e) {
    empty.style.display = "block";
    empty.innerText = "자동 백업을 불러오지 못했습니다: " + e.message;
  }
}

async function createPolicyQuick() {
  const msg = document.getElementById("pol-msg");
  let name = (document.getElementById("pol-name").value || "").trim();
  const schedule = buildCronFromQuick();

  if (!name) name = buildNameFromQuick();
  if (!name) { msg.innerText = "이름을 적어주세요."; return; }

  msg.innerText = "추가 중...";
  try {
    const r = await apiJsonRetry("/api/timeshift/policies/create", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ name, schedule })
    }, 2, 800);

    msg.innerText = (r && r.ok) ? "자동 백업이 켜졌습니다." : "추가 실패";
    document.getElementById("pol-name").value = "";

    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  } catch(e) {
    msg.innerText = "오류: " + e.message;
  }
}

async function createPolicyAdvanced() {
  const msg = document.getElementById("pol-msg");
  const name = (document.getElementById("pol-name").value || "").trim();
  const schedule = (document.getElementById("pol-schedule").value || "").trim();

  if (!name) { msg.innerText = "이름을 적어주세요."; return; }
  if (!schedule) { msg.innerText = "고급 스케줄을 입력해 주세요."; return; }

  msg.innerText = "추가 중...";
  try {
    const r = await apiJsonRetry("/api/timeshift/policies/create", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ name, schedule })
    }, 2, 900);

    msg.innerText = (r && r.ok) ? "추가 완료" : "추가 실패";
    document.getElementById("pol-name").value = "";
    document.getElementById("pol-schedule").value = "";

    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  } catch(e) {
    msg.innerText = "오류: " + e.message;
  }
}

async function updatePolicy(pid) {
  const input = document.getElementById("sched-" + pid);
  const schedule = (input ? input.value : "").trim();
  try {
    await apiJsonRetry("/api/timeshift/policies/update", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ id: pid, schedule })
    }, 2, 800);

    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  } catch(e) {
    alert("수정 실패: " + e.message);
  }
}

async function deletePolicy(pid) {
  if (!confirm("이 자동 백업을 끄시겠습니까?")) return;
  try {
    await apiJsonRetry("/api/timeshift/policies/delete", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ id: pid })
    }, 2, 800);

    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  } catch(e) {
    alert("삭제 실패: " + e.message);
  }
}

async function runPolicy(pid) {
  try {
    await apiJsonRetry("/api/timeshift/policies/run", {
      method:"POST",
      headers:{ "Content-Type":"application/json" },
      body: JSON.stringify({ id: pid })
    }, 2, 900);

    // ✅ 작업 후에만 새로고침
    await refreshAll(true);
  } catch(e) {
    alert("실행 실패: " + e.message);
  }
}

/* =========================
   새로고침 (자동 폴링 제거)
   ========================= */
async function refreshAll(markUpdated=false) {
  // 각각 독립 실행 (하나가 실패해도 나머지 뜨게)
  loadStatus();
  loadPolicies();
  loadSnapshots();

  if (markUpdated) {
    const el = document.getElementById("last-updated");
    if (el) el.innerText = "마지막 업데이트: " + nowStr();
  }
}

/* 초기 로드: 1회만 */
onQuickTypeChange();
refreshAll(true);
</script>
{% endblock %}
