document.addEventListener('DOMContentLoaded', async function() {
    const grandTotalDisplay = document.getElementById('grand-total-timbangan');
    const grandTotalOrganikDisplay = document.getElementById('grand-total-organik');
    const grandTotalAnorganikDisplay = document.getElementById('grand-total-anorganik');
    const grandTotalResiduDisplay = document.getElementById('grand-total-residu');
    const tpsContentContainer = document.getElementById('tps-content');

    let activeTpsIndex = 0;
    let tpsData = [];
    let nomorSpj = 'SPJ/07-2025/PKM/000476';
    const RECORD_DETAIL_ENDPOINT = '/upst/detail-penjemputan/api/records/detail';

    let autoSaveTimer = null;
    let autoSaveStatusEl = null;
    let loadingOverlayEl = null;
    let isAutoSaving = false;
    let pendingAutoSave = false;
    let lastAutoSaveSignature = "";

    function getLoadingOverlay() {
      if (loadingOverlayEl && document.body.contains(loadingOverlayEl)) {
        return loadingOverlayEl;
      }

      loadingOverlayEl = document.createElement('div');
      loadingOverlayEl.id = 'detail-loading-overlay';
      loadingOverlayEl.className = 'fixed inset-0 z-[9999] hidden bg-white/95 backdrop-blur-sm flex items-center justify-center px-6';
      loadingOverlayEl.innerHTML = `
        <div class="w-full max-w-sm rounded-3xl border border-gray-200 bg-white shadow-2xl px-6 py-7 text-center">
          <div class="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-upst"></div>
          <h2 class="text-base font-black text-gray-800">Sedang memuat data</h2>
          <p class="mt-2 text-sm text-gray-500">Mohon tunggu sebentar, data penjemputan sedang dipulihkan.</p>
        </div>
      `;
      document.body.appendChild(loadingOverlayEl);
      return loadingOverlayEl;
    }

    function showLoadingOverlay() {
      const overlay = getLoadingOverlay();
      overlay.classList.remove('hidden');
      document.body.style.overflow = 'hidden';
    }

    function hideLoadingOverlay() {
      const overlay = getLoadingOverlay();
      overlay.classList.add('hidden');
      document.body.style.overflow = '';
    }

    function scheduleAutoSave() {
        clearTimeout(autoSaveTimer);
        showAutoSaveStatus('menyimpan...');
      autoSaveTimer = setTimeout(autoSaveRecord, 500);
    }

    function showAutoSaveStatus(msg, isOk = false) {
        if (!autoSaveStatusEl) {
            autoSaveStatusEl = document.getElementById('auto-save-status');
        }
        if (!autoSaveStatusEl) return;
        autoSaveStatusEl.textContent = msg;
        autoSaveStatusEl.className = isOk
            ? 'text-[11px] text-green-600 text-center font-medium transition-opacity'
            : 'text-[11px] text-amber-500 text-center font-medium transition-opacity';
        autoSaveStatusEl.style.opacity = '1';
        if (isOk) setTimeout(() => { autoSaveStatusEl.style.opacity = '0'; }, 2500);
    }

    function buildAutoSavePayload(tps) {
      return {
      nomorSpj: nomorSpj || '',
      namaTps: tps.name || DEFAULT_TPS_NAME,
      lokasiAngkutId: tps.lokasiAngkutId || '',
      spjDetailId: tps.spjDetailId || '',
      latitude: tps.latitude || '',
      longitude: tps.longitude || '',
      alamatJalan: tps.alamatJalan || '',
      waktuKedatangan: tps.waktuKedatangan || '',
      fotoKedatanganFileNames: tps.fotoKedatanganFileNames || [],
      fotoKedatanganUploaded: tps.fotoKedatanganUploaded || false,
      timbangan: (tps.timbangan || []).map(t => ({
        berat: (t.berat && t.berat.length > 0) ? t.berat[0] : 0,
        jenisSampah: (t.jenisSampah && t.jenisSampah.length > 0) ? t.jenisSampah[0] : DEFAULT_JENIS,
        fotoFileName: t.fotoFileName || '',
        uploaded: t.uploaded || false,
        ocrInfo: t.ocrInfo || ''
      })),
      totalOrganik: tps.totalOrganik || 0,
      totalAnorganik: tps.totalAnorganik || 0,
      totalResidu: tps.totalResidu || 0,
      totalTimbangan: tps.totalTimbangan || 0,
      fotoPetugasFileNames: tps.fotoPetugasFileNames || [],
      fotoPetugasUploaded: tps.fotoPetugasUploaded || false,
      namaPetugas: tps.namaPetugas || ''
      };
    }

    async function autoSaveRecord() {
      const tps = tpsData[activeTpsIndex];
      if (!tps) return;
      if (tps.submitted) return;
      if (isAutoSaving) {
        pendingAutoSave = true;
        return;
      }

      const payload = buildAutoSavePayload(tps);
      const payloadSignature = JSON.stringify(payload);
      if (payloadSignature === lastAutoSaveSignature) {
        showAutoSaveStatus('✓ Data tersimpan', true);
        return;
      }

      try {
        isAutoSaving = true;
        const res = await fetch('/upst/detail-penjemputan/save-record-non-tps', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: payloadSignature
        });
        const data = await res.json();
        if (data.success) {
          lastAutoSaveSignature = payloadSignature;
        }
        showAutoSaveStatus(data.success ? '✓ Data tersimpan' : '✗ Gagal simpan', data.success);
      } catch {
        showAutoSaveStatus('✗ Gagal simpan data');
      } finally {
        isAutoSaving = false;
        if (pendingAutoSave) {
          pendingAutoSave = false;
          scheduleAutoSave();
        }
      }
    }

    function normalizeStringList(value) {
      if (!Array.isArray(value)) {
        return [];
      }

      return value.filter((item) => typeof item === 'string' && item.trim());
    }

    function normalizeJenisSampahValue(value) {
      if (Array.isArray(value)) {
        return normalizeJenisSampahValue(value[0]);
      }

      if (typeof value === 'number') {
        return JENIS_SAMPAH[value] || DEFAULT_JENIS;
      }

      if (typeof value === 'string' && value.trim()) {
        const trimmed = value.trim();
        const asNumber = Number(trimmed);
        if (!Number.isNaN(asNumber) && String(asNumber) === trimmed) {
          return JENIS_SAMPAH[asNumber] || DEFAULT_JENIS;
        }

        const matched = JENIS_SAMPAH.find(
          (item) => item.toLowerCase() === trimmed.toLowerCase(),
        );
        return matched || DEFAULT_JENIS;
      }

      return DEFAULT_JENIS;
    }

      function applyServerRecordToTps(record) {
        if (!record) return;

        const tps = tpsData[activeTpsIndex];
        if (!tps) return;
        const fotoKedatangan = normalizeStringList(
          record.fotoKedatanganFileNames || record.fotoKedatangan || record.FotoKedatangan,
        );
        const fotoPetugas = normalizeStringList(
          record.fotoPetugasFileNames || record.fotoPetugas || record.FotoPetugas,
        );

        tps.name = record.namaTps || record.name || record.Name || tps.name || DEFAULT_TPS_NAME;
        tps.lokasiAngkutId = record.lokasiAngkutId || record.LokasiAngkutID || tps.lokasiAngkutId;
        tps.spjDetailId = record.spjDetailId || record.SpjDetailID || tps.spjDetailId;
        tps.latitude = record.latitude || record.Latitude || tps.latitude;
        tps.longitude = record.longitude || record.Longitude || tps.longitude;
        tps.alamatJalan = record.alamatJalan || record.AlamatJalan || tps.alamatJalan;
        tps.waktuKedatangan = record.waktuKedatangan || record.WaktuKedatangan || tps.waktuKedatangan;
        tps.fotoKedatangan = [];
        tps.fotoKedatanganFileNames = fotoKedatangan;
        tps.fotoKedatanganUploaded = Boolean(record.fotoKedatanganUploaded ?? record.FotoKedatanganUploaded) || fotoKedatangan.length > 0;
        tps.fotoPetugas = [];
        tps.fotoPetugasFileNames = fotoPetugas;
        tps.fotoPetugasUploaded = Boolean(record.fotoPetugasUploaded ?? record.FotoPetugasUploaded) || fotoPetugas.length > 0;
        tps.namaPetugas = record.namaPetugas || record.NamaPetugas || tps.namaPetugas;
        tps.totalOrganik = Number(record.totalOrganik ?? record.TotalOrganik ?? tps.totalOrganik) || 0;
        tps.totalAnorganik = Number(record.totalAnorganik ?? record.TotalAnorganik ?? tps.totalAnorganik) || 0;
        tps.totalResidu = Number(record.totalResidu ?? record.TotalResidu ?? tps.totalResidu) || 0;
        tps.totalTimbangan = Number(record.totalTimbangan ?? record.TotalTimbangan ?? tps.totalTimbangan) || 0;
        tps.submitted = Boolean(record.isSubmit ?? record.IsSubmit ?? record.submitted ?? record.Submitted ?? tps.submitted);

        const timbangan = record.timbangan || record.Timbangan || [];
        if (Array.isArray(timbangan) && timbangan.length > 0) {
          tps.timbangan = timbangan.map(item => ({
            file: null,
            fotoFileName: item.fotoFileName || item.FotoFileName || '',
            berat: [Number(item.berat ?? (Array.isArray(item.Berat) ? item.Berat[0] : item.Berat) ?? 0) || 0],
            jenisSampah: [normalizeJenisSampahValue(item.jenisSampah ?? item.JenisSampah)],
            lokasiAngkut: [],
            uploaded: Boolean(item.uploaded ?? item.isUploaded ?? item.IsUploaded ?? item.fotoFileName ?? item.FotoFileName),
            ocrInfo: item.ocrInfo || item.OcrInfo || (item.fotoFileName || item.FotoFileName ? 'Foto dari server.' : 'OCR: diproses.')
          }));
        } else {
          tps.timbangan = [];
        }
      }

      async function loadRecordForCurrentSpj() {
        const tps = tpsData[activeTpsIndex];
        if (!tps || !nomorSpj) return;

        const params = new URLSearchParams({ nomorSpj });
        if (tps.spjDetailId) params.set('spjDetailId', tps.spjDetailId);
        if (tps.lokasiAngkutId) params.set('lokasiAngkutId', tps.lokasiAngkutId);
        if (tps.name) params.set('namaTps', tps.name);

        try {
          const res = await fetch(`${RECORD_DETAIL_ENDPOINT}?${params.toString()}`, { cache: 'no-store' });
          if (!res.ok) return;

          const data = await res.json();
          if (!data.success || !data.hasData || !data.item) return;

          applyServerRecordToTps(data.item);
        } catch (error) {
          console.warn('Gagal memuat data non-TPS:', error);
        }
      }

  const OCR_AREAS = [
    {
      id: "A",
      x: 0.34,
      y: 0.35,
      w: 0.4,
      h: 0.11,
      color: "border-lime-400 bg-lime-500/15",
    },
    {
      id: "B",
      x: 0.31,
      y: 0.33,
      w: 0.45,
      h: 0.14,
      color: "border-amber-300 bg-amber-400/10",
    },
    {
      id: "C",
      x: 0.29,
      y: 0.31,
      w: 0.49,
      h: 0.17,
      color: "border-cyan-300 bg-cyan-400/10",
    },
  ];
  const JENIS_SAMPAH = ["Organik", "Anorganik", "Residu"];
  const DEFAULT_JENIS = "Residu";
  const DETAIL_DATA_URL = "/driver/json/detail-penjemputan-non-tps.json";
  const DEFAULT_TPS_NAME = "TPS 1";

  function isBrowserFile(file) {
    return file instanceof File;
  }
  function resolveStoredPhoto(file) {
    return isBrowserFile(file) ? file : null;
  }

  function getStoredPhotoUrl(file) {
    if (isBrowserFile(file)) return URL.createObjectURL(file);
    if (typeof file === 'string') return file;
    return '';
  }

  function getStoredPhotoName(file, defaultName = 'Foto') {
    if (isBrowserFile(file)) return file.name;
    if (typeof file === 'string') return file.split('/').pop() || defaultName;
    return defaultName;
  }

  function getStoredPhotoSize(file) {
    if (isBrowserFile(file)) return file.size;
    return 0;
  }

    function initializeLocation() {
        tpsData = [{
            name: DEFAULT_TPS_NAME,
            index: 0,
            lokasiAngkutId: '',
            spjDetailId: '',
            latitude: '',
            longitude: '',
            alamatJalan: '',
            waktuKedatangan: '',
            fotoKedatangan: [],
            fotoKedatanganFileNames: [],
            fotoKedatanganUploaded: false,
            timbangan: [],
            totalOrganik: 0,
            totalAnorganik: 0,
            totalResidu: 0,
            totalTimbangan: 0,
            fotoPetugas: [],
            fotoPetugasFileNames: [],
            fotoPetugasUploaded: false,
            namaPetugas: '',
            submitted: false
        }];
    }

  async function loadDetailData() {
    try {
      const response = await fetch(DETAIL_DATA_URL, { cache: "no-store" });
      if (!response.ok) {
        return;
      }

      const payload = await response.json();
      const detail = payload.detailPenjemputan || payload;
      const namaTps =
        detail.namaTps || detail.tpsName || detail.name || DEFAULT_TPS_NAME;
      const namaPerusahaan = detail.namaPerusahaan || detail.companyName || "";

            if (tpsData[0]) {
                tpsData[0].name = namaTps;
                tpsData[0].lokasiAngkutId = detail.lokasiAngkutId || detail.LokasiAngkutID || tpsData[0].lokasiAngkutId;
                tpsData[0].spjDetailId = detail.spjDetailId || detail.SpjDetailID || tpsData[0].spjDetailId;
            }

            nomorSpj = detail.nomorSpj || nomorSpj;
            applyDetailDataToView(detail, namaTps, namaPerusahaan);
            const _form = tpsContentContainer.querySelector('form');
            if (_form) {
                const lokasiInput = _form.querySelector('.tps-lokasi-angkut-id');
                const spjInput = _form.querySelector('.tps-spj-detail-id');
                if (lokasiInput) lokasiInput.value = tpsData[0].lokasiAngkutId;
                if (spjInput) spjInput.value = tpsData[0].spjDetailId;
            }
        } catch (error) {
            console.warn('Gagal memuat detail penjemputan non-TPS:', error);
        }
    }

  function applyDetailDataToView(detail, namaTps, namaPerusahaan) {
    const titleEl = document.getElementById("detail-page-title");
    const badgeEl = document.getElementById("detail-tps-badge");
    const formBadgeEl = document.getElementById("detail-form-badge");
    const companyEl = document.getElementById("detail-company-name");
    const spjEl = document.getElementById("detail-spj-number");
    const addressEl = document.getElementById("detail-address");
    const platEl = document.getElementById("plat-nomor");
    const doorEl = document.getElementById("detail-nomor-pintu");

    if (titleEl) titleEl.textContent = namaTps;
    if (badgeEl) badgeEl.textContent = namaTps;
    if (formBadgeEl) formBadgeEl.textContent = namaTps;
    if (companyEl && namaPerusahaan) companyEl.textContent = namaPerusahaan;
    if (spjEl && detail.nomorSpj) spjEl.textContent = detail.nomorSpj;
    if (addressEl && detail.alamat) addressEl.textContent = detail.alamat;
    if (platEl && detail.platNomor) platEl.textContent = detail.platNomor;
    if (doorEl && detail.nomorPintu) doorEl.textContent = detail.nomorPintu;
    document.title = `Detail Penjemputan - ${namaTps}`;
  }

  function renderTpsForm() {
    const tps = tpsData[activeTpsIndex];
    const submitState = getSubmitState(tps);
    const actionMarkup = tps.submitted
      ? `
      <div class="flex items-center justify-center gap-2 rounded-xl border border-green-200 bg-green-50 px-4 py-3 text-sm">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 flex-shrink-0 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
            <path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>
          <span class="font-medium text-green-700">
            Data <strong class="font-bold text-green-800">${tps.name || DEFAULT_TPS_NAME} </strong> sudah disubmit
          </span>
        </div>`
      : `
          <div class="flex gap-3">
            <a href="/upst/detail-penjemputan/batal" class="w-1/3 text-center bg-red-500 text-white py-3 rounded-xl font-bold text-sm">Batal</a>
            <button type="submit" ${submitState.canSubmit ? "" : "disabled"} class="w-2/3 py-3 rounded-xl font-bold text-sm transition ${submitState.canSubmit ? "bg-upst text-white hover:brightness-110" : "bg-gray-300 text-gray-500 cursor-not-allowed"}">Submit</button>
          </div>
          ${submitState.canSubmit ? '' : `<p class="submit-state-message text-[11px] text-center text-red-500 font-medium">${submitState.message}</p>`}
        `;

    tpsContentContainer.innerHTML = `
            <form class="space-y-5 pb-8" data-tps-index="${tps.index}">
                <input type="hidden" class="tps-lokasi-angkut-id" value="${tps.lokasiAngkutId || ""}" />
                <input type="hidden" class="tps-spj-detail-id" value="${tps.spjDetailId || ""}" />
                <input type="hidden" class="tps-latitude" value="${tps.latitude}" />
                <input type="hidden" class="tps-longitude" value="${tps.longitude}" />
                <input type="hidden" class="tps-alamat-jalan" value="${tps.alamatJalan}" />
                <input type="hidden" class="tps-total-timbangan" value="${tps.totalTimbangan}" />
                <input type="hidden" class="tps-total-organik" value="${tps.totalOrganik}" />
                <input type="hidden" class="tps-total-anorganik" value="${tps.totalAnorganik}" />
                <input type="hidden" class="tps-total-residu" value="${tps.totalResidu}" />

                <section class="bg-white border border-gray-100 rounded-3xl p-5 space-y-4">
                    <div class="flex items-center gap-3">
                        <div class="w-8 h-8 rounded-full bg-upst text-white font-black text-sm flex items-center justify-center">1</div>
                        <div>
                            <h3 class="font-black text-gray-800">Foto Kedatangan </h3>
                            <p class="text-xs text-gray-500">Upload foto kedatangan</p>
                        </div>
                    </div>

                    <label class="block text-xs font-semibold text-gray-600">Upload Foto Kedatangan</label>
                    <input type="file" class="tps-foto-kedatangan block w-full text-sm text-gray-700 border border-gray-200 rounded-xl p-2 file:mr-3 file:rounded-lg file:border-0 file:bg-upst file:px-3 file:py-2 file:text-xs file:font-bold file:text-white" accept="image/*" />
                    <div class="tps-preview-kedatangan space-y-2"></div>

                    <div class="kedatangan-upload-state">${getKedatanganUploadStateMarkup(tps)}</div>

                    <div class="grid grid-cols-2 gap-2">
                        <div>
                            <label class="block text-xs font-semibold text-gray-600 mb-1">Latitude</label>
                            <input type="text" class="tps-display-latitude w-full rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 text-xs" readonly value="${tps.latitude}" />
                        </div>
                        <div>
                            <label class="block text-xs font-semibold text-gray-600 mb-1">Longitude</label>
                            <input type="text" class="tps-display-longitude w-full rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 text-xs" readonly value="${tps.longitude}" />
                        </div>
                    </div>

                    <div>
                        <label class="block text-xs font-semibold text-gray-600 mb-1">Waktu Kedatangan</label>
                        <input type="text" class="tps-waktu-kedatangan w-full rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 text-xs" readonly value="${tps.waktuKedatangan}" />
                    </div>
                </section>

                <section class="bg-white border border-gray-100 rounded-3xl p-5 space-y-4">
                    <div class="flex items-center gap-3">
                        <div class="w-8 h-8 rounded-full bg-upst text-white font-black text-sm flex items-center justify-center">2</div>
                        <div>
                            <h3 class="font-black text-gray-800">Foto Timbang Sampah </h3>
                            <p class="text-xs text-gray-500">Upload foto timbangan, berat auto terisi</p>
                        </div>
                    </div>

                    <div class="tps-timbangan-repeater space-y-3"></div>

                    <button type="button" class="tps-btn-add-timbangan w-full border border-dashed border-upst text-upst rounded-xl py-2 text-xs font-bold transition">
                        + Tambah Foto Timbangan
                    </button>
                </section>

                <section class="bg-white border border-gray-100 rounded-3xl p-5 space-y-4">
                    <div class="flex items-center gap-3">
                        <div class="w-8 h-8 rounded-full bg-upst text-white font-black text-sm flex items-center justify-center">3</div>
                        <div>
                            <h3 class="font-black text-gray-800">Foto Petugas </h3>
                            <p class="text-xs text-gray-500">Upload dokumentasi petugas</p>
                        </div>
                    </div>

                    <label class="block text-xs font-semibold text-gray-600">Upload Foto Petugas</label>
                    <input type="file" class="tps-foto-petugas block w-full text-sm text-gray-700 border border-gray-200 rounded-xl p-2 file:mr-3 file:rounded-lg file:border-0 file:bg-upst file:px-3 file:py-2 file:text-xs file:font-bold file:text-white" accept="image/*" />
                    <div class="tps-preview-petugas space-y-2"></div>

                    <div class="petugas-upload-state">${getPetugasUploadStateMarkup(tps)}</div>

                    <div>
                        <label class="block text-xs font-semibold text-gray-600 mb-1">Nama Petugas</label>
                        <input type="text" class="tps-nama-petugas w-full rounded-xl border border-gray-200 px-3 py-2 text-sm" placeholder="Masukkan nama petugas" value="${tps.namaPetugas}" />
                    </div>
                </section>

                    ${actionMarkup}
                <p id="auto-save-status" class="text-[11px] text-amber-500 text-center font-medium" style="opacity:0;transition:opacity 0.4s"></p>
            </form>
        `;

    attachTpsFormListeners();
    restoreTpsTimbanganItems();
    restorePhotoPreview();
  }

  function restorePhotoPreview() {
    const tps = tpsData[activeTpsIndex];
    const form = tpsContentContainer.querySelector("form");
    if (!form) return;

        const previewKedatangan = form.querySelector('.tps-preview-kedatangan');
        if (previewKedatangan) {
            if (tps.fotoKedatangan.length > 0) {
                renderStoredPhotos(tps.fotoKedatangan, previewKedatangan);
            } else if (tps.fotoKedatanganUploaded && tps.fotoKedatanganFileNames.length > 0) {
                renderServerImagePreview(tps.fotoKedatanganFileNames, previewKedatangan);
            }
        }

        const previewPetugas = form.querySelector('.tps-preview-petugas');
        if (previewPetugas) {
            if (tps.fotoPetugas.length > 0) {
                renderStoredPhotos(tps.fotoPetugas, previewPetugas);
            } else if (tps.fotoPetugasUploaded && tps.fotoPetugasFileNames.length > 0) {
                renderServerImagePreview(tps.fotoPetugasFileNames, previewPetugas);
            }
        }
    }

  function renderStoredPhotos(files, container) {
    container.innerHTML = "";
    container.className = "space-y-2";

    files.forEach((file, index) => {
      const item = document.createElement("div");
      item.className =
        "rounded-xl border border-gray-200 overflow-hidden bg-black";

      const imageUrl = getStoredPhotoUrl(file);
      const safeName = getStoredPhotoName(file, `Foto ${index + 1}`).replace(
        /"/g,
        "&quot;",
      );
      const fileSize = getStoredPhotoSize(file);
      item.innerHTML = `
                <div class="h-44 bg-black/80">
                    <img src="${imageUrl}" alt="Preview ${index + 1}" class="w-full h-full object-contain preview-multi-image" />
                </div>
            `;

      const img = item.querySelector(".preview-multi-image");
      if (img && isBrowserFile(resolveStoredPhoto(file))) {
        img.onload = function () {
          URL.revokeObjectURL(imageUrl);
        };
      }
      container.appendChild(item);
    });
  }

  function attachTpsFormListeners() {
    const form = tpsContentContainer.querySelector("form");
    const tps = tpsData[activeTpsIndex];

    if (tps.submitted) return;

    const fotoKedatanganInput = form.querySelector(".tps-foto-kedatangan");
    const fotoPetugasInput = form.querySelector(".tps-foto-petugas");
    const namaPetugasInput = form.querySelector(".tps-nama-petugas");
    const btnAddTimbangan = form.querySelector(".tps-btn-add-timbangan");

        fotoKedatanganInput.addEventListener('change', function() {
            tps.fotoKedatangan = Array.from(this.files);
            tps.fotoKedatanganUploaded = false;
            updateWaktuKedatangan();
            updateMultiPreview(this, form.querySelector('.tps-preview-kedatangan'));
            refreshKedatanganUploadState(form);
        });

        fotoPetugasInput.addEventListener('change', function() {
            tps.fotoPetugas = Array.from(this.files);
            tps.fotoPetugasUploaded = false;
            updateMultiPreview(this, form.querySelector('.tps-preview-petugas'));
            refreshPetugasUploadState(form);
        });

        namaPetugasInput.addEventListener('input', function() {
            tps.namaPetugas = this.value;
            refreshPetugasUploadState(form);
          scheduleAutoSave();
        });

        namaPetugasInput.addEventListener('blur', function() {
            tps.namaPetugas = this.value;
            scheduleAutoSave();
        });

    btnAddTimbangan.addEventListener("click", function () {
      createTimbanganItem(form.querySelector(".tps-timbangan-repeater"));
      syncTimbanganToTpsData();
      refreshSubmitButtonState(form);
    });

    const btnUploadKedatangan = form.querySelector(
      ".tps-btn-upload-kedatangan",
    );
    if (btnUploadKedatangan) {
      btnUploadKedatangan.addEventListener("click", uploadFotoKedatangan);
    }

    const btnUploadPetugas = form.querySelector(".tps-btn-upload-petugas");
    if (btnUploadPetugas) {
      btnUploadPetugas.addEventListener("click", uploadFotoPetugas);
    }

    form.addEventListener("submit", function (e) {
      e.preventDefault();
      submitTpsData();
    });
  }

  function restoreTpsTimbanganItems() {
    const tps = tpsData[activeTpsIndex];
    const form = tpsContentContainer.querySelector("form");
    const repeater = form.querySelector(".tps-timbangan-repeater");

    if (tps.timbangan.length === 0) {
      createTimbanganItem(repeater);
    } else {
      tps.timbangan.forEach((timb) => createTimbanganItem(repeater, timb));
    }
  }

  function updateWaktuKedatangan() {
    const tps = tpsData[activeTpsIndex];
    const now = new Date();
    const formatted = now.toLocaleString("id-ID", {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
    });
    tps.waktuKedatangan = formatted;

    const form = tpsContentContainer.querySelector("form");
    const displayWaktu = form.querySelector(".tps-waktu-kedatangan");
    if (displayWaktu) displayWaktu.value = formatted;

    getLocationUpdate();
  }

  function reverseGeocode(lat, lng) {
    const tps = tpsData[activeTpsIndex];
    fetch(
      `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`,
    )
      .then((res) => res.json())
      .then((data) => {
        const address = data.display_name || `${lat}, ${lng}`;
        tps.latitude = lat;
        tps.longitude = lng;
        tps.alamatJalan = address;

        const form = tpsContentContainer.querySelector("form");
        if (form) {
          const latInput = form.querySelector(".tps-display-latitude");
          const lngInput = form.querySelector(".tps-display-longitude");
          if (latInput) latInput.value = lat;
          if (lngInput) lngInput.value = lng;
        }
      })
      .catch(() => {
        tps.latitude = lat;
        tps.longitude = lng;
        tps.alamatJalan = `${lat}, ${lng}`;

        const form = tpsContentContainer.querySelector("form");
        if (form) {
          const latInput = form.querySelector(".tps-display-latitude");
          const lngInput = form.querySelector(".tps-display-longitude");
          if (latInput) latInput.value = lat;
          if (lngInput) lngInput.value = lng;
        }
      });
  }

  function getLocationUpdate() {
    if (!("geolocation" in navigator)) return;

    navigator.geolocation.getCurrentPosition(
      function (position) {
        const lat = position.coords.latitude.toFixed(6);
        const lng = position.coords.longitude.toFixed(6);
        reverseGeocode(lat, lng);
      },
      function () {
        console.log("Lokasi tidak diizinkan");
      },
    );
  }

  function formatFileSize(bytes) {
    const mb = bytes / (1024 * 1024);
    return `${mb.toFixed(2)} MB`;
  }

  function updateMultiPreview(input, previewContainer) {
    if (!input || !previewContainer) return;

    previewContainer.innerHTML = "";
    previewContainer.className = "space-y-2";

    if (!input.files || input.files.length === 0) return;

    Array.from(input.files).forEach((file, index) => {
      const item = document.createElement("div");
      item.className =
        "rounded-xl border border-gray-200 overflow-hidden bg-black";

      const imageUrl = URL.createObjectURL(file);
      const safeName = file.name.replace(/"/g, "&quot;");
      item.innerHTML = `
                <div class="h-44 bg-black/80">
                    <img src="${imageUrl}" alt="Preview ${index + 1}" class="w-full h-full object-contain preview-multi-image" />
                </div>
            `;

      const img = item.querySelector(".preview-multi-image");
      if (img) {
        img.onload = function () {
          URL.revokeObjectURL(imageUrl);
        };
      }
      previewContainer.appendChild(item);
    });
  }

  function formatWeightDisplay(value) {
    if (isNaN(value)) return "0,00";
    return value.toFixed(2).replace(".", ",");
  }

  function parseWeightInput(value) {
    if (!value) return 0;
    const cleaned = value
      .toString()
      .trim()
      .replace(/\s/g, "")
      .replace(",", ".");
    const parsed = parseFloat(cleaned);
    return isNaN(parsed) ? 0 : parsed;
  }

  async function applyWatermark(file, photoNumber) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = function (e) {
        const img = new Image();
        img.onload = function () {
          const canvas = document.createElement("canvas");
          canvas.width = img.width;
          canvas.height = img.height;
          const ctx = canvas.getContext("2d");
          ctx.drawImage(img, 0, 0);

          const now = new Date();
          const days = [
            "Minggu",
            "Senin",
            "Selasa",
            "Rabu",
            "Kamis",
            "Jumat",
            "Sabtu",
          ];
          const months = [
            "Jan",
            "Feb",
            "Mar",
            "Apr",
            "Mei",
            "Jun",
            "Jul",
            "Agu",
            "Sep",
            "Okt",
            "Nov",
            "Des",
          ];
          const timestamp = `${days[now.getDay()]}, ${now.getDate().toString().padStart(2, "0")} ${months[now.getMonth()]} ${now.getFullYear()} • ${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}`;

          const baseFontSize = Math.max(
            16,
            Math.min(img.width, img.height) * 0.025,
          );
          const fontFamily = "'Montserrat', 'Segoe UI', 'Roboto', sans-serif";
          const lines = [
            {
              text: `FOTO TIMBANG #${photoNumber}`,
              size: baseFontSize * 1.1,
              weight: "900",
              color: "#FFD700",
            },
            {
              text: `${nomorSpj}`,
              size: baseFontSize * 0.9,
              weight: "700",
              color: "#FFFFFF",
            },
            {
              text: timestamp,
              size: baseFontSize * 0.75,
              weight: "500",
              color: "#E2E8F0",
            },
          ];

          const paddingX = baseFontSize * 1.2;
          const paddingY = baseFontSize * 1.0;
          const lineGap = baseFontSize * 0.4;
          let maxWidth = 0;
          let totalHeight = 0;
          lines.forEach((line) => {
            ctx.font = `${line.weight} ${line.size}px ${fontFamily}`;
            const metrics = ctx.measureText(line.text);
            maxWidth = Math.max(maxWidth, metrics.width);
            totalHeight += line.size + lineGap;
          });
          totalHeight -= lineGap;

          const margin = baseFontSize * 1.5;
          const boxWidth = maxWidth + paddingX * 2;
          const boxHeight = totalHeight + paddingY * 2;
          const boxX = img.width - boxWidth - margin;
          const boxY = img.height - boxHeight - margin;

          ctx.beginPath();
          if (ctx.roundRect) {
            ctx.roundRect(boxX, boxY, boxWidth, boxHeight, baseFontSize * 0.8);
          } else {
            ctx.rect(boxX, boxY, boxWidth, boxHeight);
          }
          ctx.fillStyle = "rgba(15, 23, 42, 0.85)";
          ctx.fill();

          const accentWidth = baseFontSize * 0.3;
          ctx.beginPath();
          if (ctx.roundRect) {
            ctx.roundRect(
              boxX + boxWidth - accentWidth,
              boxY,
              accentWidth,
              boxHeight,
              [0, baseFontSize * 0.8, baseFontSize * 0.8, 0],
            );
          } else {
            ctx.rect(
              boxX + boxWidth - accentWidth,
              boxY,
              accentWidth,
              boxHeight,
            );
          }
          ctx.fillStyle = "#FFD700";
          ctx.fill();

          ctx.textAlign = "right";
          ctx.textBaseline = "top";
          let currentY = boxY + paddingY;
          const textRightLimit = boxX + boxWidth - paddingX - accentWidth;
          lines.forEach((line) => {
            ctx.font = `${line.weight} ${line.size}px ${fontFamily}`;
            ctx.fillStyle = line.color;
            ctx.fillText(line.text, textRightLimit, currentY);
            currentY += line.size + lineGap;
          });

          canvas.toBlob(
            function (blob) {
              const watermarkedFile = new File([blob], file.name, {
                type: "image/jpeg",
                lastModified: Date.now(),
              });
              resolve(watermarkedFile);
            },
            "image/jpeg",
            0.95,
          );
        };
        img.onerror = reject;
        img.src = e.target.result;
      };
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  }

  function readFileAsImage(file) {
    return new Promise(function (resolve, reject) {
      const objectUrl = URL.createObjectURL(file);
      const img = new Image();
      img.onload = function () {
        URL.revokeObjectURL(objectUrl);
        resolve(img);
      };
      img.onerror = function () {
        URL.revokeObjectURL(objectUrl);
        reject(new Error("Gagal membaca gambar."));
      };
      img.src = objectUrl;
    });
  }

  function createCropCanvas(img, area) {
    const sx = Math.max(0, Math.floor(img.width * area.x));
    const sy = Math.max(0, Math.floor(img.height * area.y));
    const sw = Math.max(1, Math.floor(img.width * area.w));
    const sh = Math.max(1, Math.floor(img.height * area.h));
    const canvas = document.createElement("canvas");
    const scale = 2.2;
    canvas.width = Math.max(1, Math.floor(sw * scale));
    canvas.height = Math.max(1, Math.floor(sh * scale));
    const ctx = canvas.getContext("2d");
    if (!ctx) return canvas;
    ctx.imageSmoothingEnabled = true;
    ctx.drawImage(img, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height);
    return canvas;
  }

  function canvasToJpegFile(canvas, fileName) {
    return new Promise(function (resolve, reject) {
      canvas.toBlob(
        function (blob) {
          if (!blob) {
            reject(new Error("Gagal membuat crop image."));
            return;
          }
          resolve(new File([blob], fileName, { type: "image/jpeg" }));
        },
        "image/jpeg",
        0.92,
      );
    });
  }

  async function requestOpenRouterWeight(imageFile) {
    const formData = new FormData();
    formData.append("Foto", imageFile);
    const response = await fetch("/upst/detail-penjemputan/ocr-timbangan", {
      method: "POST",
      body: formData,
    });
    const result = await response.json();
    if (!response.ok) throw new Error(result.message || "Request OCR gagal.");
    return result;
  }

  async function autoFillWeight(file, weightInput, ocrInfoEl) {
    let guessedWeight = 0;
    weightInput.placeholder = "Membaca angka dari foto...";
    if (ocrInfoEl) ocrInfoEl.textContent = "AI: memproses gambar...";

    try {
      const img = await readFileAsImage(file);
      let bestRawText = "";
      let isSuccess = false;

      for (const area of OCR_AREAS) {
        const cropCanvas = createCropCanvas(img, area);
        const cropFile = await canvasToJpegFile(
          cropCanvas,
          `crop-${area.id}.jpg`,
        );
        const aiResult = await requestOpenRouterWeight(cropFile);

        if (aiResult && aiResult.success && aiResult.weight) {
          guessedWeight = parseWeightInput(aiResult.weight);
          bestRawText = aiResult.raw || aiResult.weight;
          isSuccess = guessedWeight > 0;
          if (isSuccess) break;
        }

        if (aiResult && aiResult.raw) bestRawText = aiResult.raw;
      }

      if (ocrInfoEl) {
        const cleaned = (bestRawText || "").replace(/\s+/g, " ").trim();
        ocrInfoEl.textContent = isSuccess
          ? `AI terbaca: ${cleaned}`
          : cleaned
            ? `AI tidak valid: ${cleaned}`
            : "AI tidak menemukan angka valid.";
      }
    } catch (_) {
      guessedWeight = 0;
      if (ocrInfoEl) ocrInfoEl.textContent = "AI gagal diproses.";
    }

    if (guessedWeight > 0) {
      weightInput.value = formatWeightDisplay(guessedWeight);
      weightInput.placeholder = "Berat terdeteksi otomatis";
    } else {
      weightInput.placeholder = "Tidak terbaca otomatis, isi manual";
    }

    updateTpsTotalTimbangan();
  }

  function updateTpsTotalTimbangan() {
    const tps = tpsData[activeTpsIndex];
    const form = tpsContentContainer.querySelector("form");
    if (!form) return;

    let totalOrganik = 0.0;
    let totalAnorganik = 0.0;
    let totalResidu = 0.0;
    const repeater = form.querySelector(".tps-timbangan-repeater");
    const items = repeater.querySelectorAll(".timbangan-item");

    items.forEach(function (item) {
      const weightInput = item.querySelector(".input-berat-timbangan-value");
      const jenisSampahSelect = item.querySelector(".input-jenis-sampah");
      if (weightInput && jenisSampahSelect) {
        const value = parseWeightInput(weightInput.value || "0");
        const jenis = jenisSampahSelect.value;
        if (jenis === "Organik") totalOrganik += value;
        else if (jenis === "Anorganik") totalAnorganik += value;
        else totalResidu += value;
      }
    });

    tps.totalOrganik = totalOrganik;
    tps.totalAnorganik = totalAnorganik;
    tps.totalResidu = totalResidu;
    tps.totalTimbangan = totalOrganik + totalAnorganik + totalResidu;

    const displayTotal = form.querySelector(".tps-display-total");
    if (displayTotal)
      displayTotal.textContent = formatWeightDisplay(tps.totalTimbangan);

    if (grandTotalDisplay)
      grandTotalDisplay.textContent = formatWeightDisplay(tps.totalTimbangan);
    if (grandTotalOrganikDisplay)
      grandTotalOrganikDisplay.textContent = formatWeightDisplay(totalOrganik);
    if (grandTotalAnorganikDisplay)
      grandTotalAnorganikDisplay.textContent =
        formatWeightDisplay(totalAnorganik);
    if (grandTotalResiduDisplay)
      grandTotalResiduDisplay.textContent = formatWeightDisplay(totalResidu);
  }

    function getTimbanganUploadStateMarkup(hasFile, isUploaded, hasValidWeight) {
        if (isUploaded) {
            return `
                <div class="text-center text-xs text-green-600 font-bold py-2">✓ Foto timbangan sudah diupload</div>
                <p class="text-[11px] text-gray-500 text-center">Jika ingin revisi, pilih file baru diatas. Status upload akan tereset otomatis.</p>
            `;
        }

        if (!hasFile) {
            return '<p class="text-[11px] text-gray-400">Pilih foto timbangan terlebih dahulu</p>';
        }

    if (!hasValidWeight) {
      return `
                <button type="button" disabled class="btn-upload-timbangan w-full bg-gray-300 text-gray-500 py-2 rounded-xl font-bold text-xs cursor-not-allowed">Upload Foto Timbangan Ini</button>
                <p class="text-[11px] text-red-500 text-center">Isi berat manual dulu sebelum upload jika berat tidak terbaca.</p>
            `;
    }

    return `
            <button type="button" class="btn-upload-timbangan w-full bg-blue-500 text-white py-2 rounded-xl font-bold text-xs hover:brightness-110">Upload Foto Timbangan Ini</button>
            <p class="text-[11px] text-amber-600 text-center">Foto siap diupload.</p>
        `;
  }

  function getKedatanganUploadStateMarkup(tps) {
    if (tps.fotoKedatanganUploaded) {
      return '<div class="text-center text-xs text-green-600 font-bold py-2">✓ Foto kedatangan sudah diupload</div>';
    }
    if (!tps.fotoKedatangan.length) {
      return "";
    }
    return `<button type="button" class="tps-btn-upload-kedatangan w-full bg-blue-500 text-white py-2 rounded-xl font-bold text-xs hover:brightness-110">Upload ${tps.fotoKedatangan.length} Foto Kedatangan</button>`;
  }

  function refreshKedatanganUploadState(form) {
    const stateContainer = form.querySelector(".kedatangan-upload-state");
    if (!stateContainer) return;

    const tps = tpsData[activeTpsIndex];
    stateContainer.innerHTML = getKedatanganUploadStateMarkup(tps);

    const btnUploadKedatangan = stateContainer.querySelector(
      ".tps-btn-upload-kedatangan",
    );
    if (btnUploadKedatangan) {
      btnUploadKedatangan.addEventListener("click", uploadFotoKedatangan);
    }

    refreshSubmitButtonState(form);
  }

  function getPetugasUploadStateMarkup(tps) {
    if (tps.fotoPetugasUploaded) {
      return '<div class="text-center text-xs text-green-600 font-bold py-2">✓ Foto petugas sudah diupload</div>';
    }

    if (!tps.fotoPetugas.length) {
      return "";
    }

    if (!tps.namaPetugas.trim()) {
      return `
                <button type="button" disabled class="tps-btn-upload-petugas w-full bg-gray-300 text-gray-500 py-2 rounded-xl font-bold text-xs cursor-not-allowed">Upload ${tps.fotoPetugas.length} Foto Petugas</button>
                <p class="text-[11px] text-red-500 text-center">Isi nama petugas terlebih dahulu</p>
            `;
    }

    return `<button type="button" class="tps-btn-upload-petugas w-full bg-blue-500 text-white py-2 rounded-xl font-bold text-xs hover:brightness-110">Upload ${tps.fotoPetugas.length} Foto Petugas</button>`;
  }

  function refreshPetugasUploadState(form) {
    const stateContainer = form.querySelector(".petugas-upload-state");
    if (!stateContainer) return;

    const tps = tpsData[activeTpsIndex];
    stateContainer.innerHTML = getPetugasUploadStateMarkup(tps);

    const btnUploadPetugas = stateContainer.querySelector(
      ".tps-btn-upload-petugas:not([disabled])",
    );
    if (btnUploadPetugas) {
      btnUploadPetugas.addEventListener("click", uploadFotoPetugas);
    }

    refreshSubmitButtonState(form);
  }

    function isTimbanganItemReady(timbanganItem) {
        const weight = timbanganItem?.berat && timbanganItem.berat.length > 0 ? timbanganItem.berat[0] : 0;
        const hasFile = Boolean(timbanganItem?.file) || Boolean(timbanganItem?.fotoFileName);
        return hasFile && Boolean(timbanganItem?.uploaded) && weight > 0;
    }

    function getSubmitState(tps) {
      if (tps?.submitted) {
        return { canSubmit: false, message: '' };
      }

        if (!tps.fotoKedatanganUploaded) {
            if (!tps.fotoKedatangan.length)
                return { canSubmit: false, message: 'Silakan pilih dan upload foto kedatangan.' };
            return { canSubmit: false, message: 'Silakan upload foto kedatangan terlebih dahulu.' };
        }

    if (!tps.timbangan.length) {
      return {
        canSubmit: false,
        message: "Tambahkan minimal 1 data timbangan sebelum submit.",
      };
    }

    if (tps.timbangan.some((item) => !isTimbanganItemReady(item))) {
      return {
        canSubmit: false,
        message:
          "Pastikan semua foto timbangan sudah diupload dan beratnya valid.",
      };
    }

        if (!tps.fotoPetugasUploaded) {
            if (!tps.fotoPetugas.length)
                return { canSubmit: false, message: 'Silakan pilih dan upload foto petugas.' };
            return { canSubmit: false, message: 'Silakan upload foto petugas terlebih dahulu.' };
        }

    if (!tps.namaPetugas.trim()) {
      return {
        canSubmit: false,
        message: "Isi nama petugas dulu sebelum submit.",
      };
    }

    return { canSubmit: true, message: "" };
  }

  function refreshSubmitButtonState(form) {
    const submitButton = form.querySelector('button[type="submit"]');
    const tps = tpsData[activeTpsIndex];
    if (tps?.submitted || !submitButton) return;

    const helperText = form.querySelector(".submit-state-message");
    const submitState = getSubmitState(tps);

    submitButton.disabled = !submitState.canSubmit;
    submitButton.className = `w-2/3 py-3 rounded-xl font-bold text-sm transition ${submitState.canSubmit ? "bg-upst text-white hover:brightness-110" : "bg-gray-300 text-gray-500 cursor-not-allowed"}`;

    let messageEl = helperText;
    if (!messageEl) {
      messageEl = document.createElement("p");
      messageEl.className =
        "submit-state-message text-[11px] text-center text-red-500 font-medium";
      submitButton
        .closest(".flex.gap-3")
        ?.insertAdjacentElement("afterend", messageEl);
    }

    if (submitState.canSubmit) {
      messageEl.textContent = "";
      messageEl.classList.add("hidden");
    } else {
      messageEl.textContent = submitState.message;
      messageEl.classList.remove("hidden");
    }
  }

  function refreshTimbanganUploadState(item) {
    const stateContainer = item.querySelector(".timbangan-upload-state");
    if (!stateContainer) return;

        const repeater = item.parentElement;
        const itemIndex = repeater ? Array.from(repeater.children).indexOf(item) : -1;
        const tps = tpsData[activeTpsIndex];
        const currentData = itemIndex >= 0 ? tps.timbangan[itemIndex] : null;
        const fileInput = item.querySelector('.input-foto-timbangan');
        const hasFile = Boolean(currentData?.file || fileInput?.files?.[0] || currentData?.fotoFileName);
        const isUploaded = Boolean(currentData?.uploaded);
        const weightInputValue = item.querySelector('.input-berat-timbangan-value');
        const currentWeight = currentData?.berat && currentData.berat.length > 0
            ? currentData.berat[0]
            : parseWeightInput(weightInputValue?.value || '0');
        const hasValidWeight = currentWeight > 0;

    stateContainer.innerHTML = getTimbanganUploadStateMarkup(
      hasFile,
      isUploaded,
      hasValidWeight,
    );

    const uploadBtn = stateContainer.querySelector(".btn-upload-timbangan");
    if (uploadBtn) {
      uploadBtn.addEventListener("click", function () {
        const latestIndex = repeater
          ? Array.from(repeater.children).indexOf(item)
          : -1;
        uploadSingleFotoTimbangan(latestIndex, item);
      });
    }

    const form = tpsContentContainer.querySelector("form");
    if (form) {
      refreshSubmitButtonState(form);
    }
  }

  function renumberTimbanganItems(repeater) {
    const items = repeater.querySelectorAll(".timbangan-item");
    items.forEach((item, index) => {
      const newNumber = index + 1;
      item.dataset.photoNumber = newNumber;

      const label = item.querySelector(".text-xs.font-bold.text-gray-600");
      if (label) {
        label.textContent = `Item Timbangan #${newNumber}`;
      }
    });
  }

  function createTimbanganItem(repeater, existingData = null) {
    const photoNumber = repeater.children.length + 1;
    const item = document.createElement("div");
    item.className =
      "timbangan-item rounded-2xl border border-gray-200 p-3 space-y-2 bg-gray-50";
    item.dataset.photoNumber = photoNumber;

        const weight = existingData ? (existingData.berat && existingData.berat.length > 0 ? existingData.berat[0] : 0) : 0;
        const jenisSampah = existingData ? (existingData.jenisSampah && existingData.jenisSampah.length > 0 ? existingData.jenisSampah[0] : DEFAULT_JENIS) : DEFAULT_JENIS;
        const hasFileBlob = Boolean(existingData?.file);
        const hasFile = Boolean(existingData?.file || existingData?.fotoFileName);
        const isUploaded = Boolean(existingData?.uploaded);
        const ocrInfoText = existingData && existingData.ocrInfo ? existingData.ocrInfo : (hasFile ? 'OCR: diproses.' : 'OCR: belum diproses.');

    item.innerHTML = `
            <div class="flex items-center justify-between">
                <p class="text-xs font-bold text-gray-600">Item Timbangan #${photoNumber}</p>
                <button type="button" class="btn-remove-timbangan text-[11px] font-bold text-red-500">Hapus</button>
            </div>
            <input type="file" name="FotoTimbangan" accept="image/*" class="input-foto-timbangan block w-full text-sm text-gray-700 border border-gray-200 rounded-xl p-2 file:mr-3 file:rounded-lg file:border-0 file:bg-upst file:px-3 file:py-2 file:text-xs file:font-bold file:text-white" />
            <div class="input-preview-wrap ${(hasFileBlob || (hasFile && existingData?.fotoFileName?.startsWith('/'))) ? '' : 'hidden'} relative rounded-xl overflow-hidden border border-gray-200 bg-black">
                <img class="input-preview-image w-full h-44 object-contain" alt="Preview foto timbangan" />
                <div class="input-crop-overlay absolute inset-0 pointer-events-none"></div>
            </div>
            <p class="text-[11px] text-gray-500 input-ocr-info">${ocrInfoText}</p>
            <div class="timbangan-upload-state">${getTimbanganUploadStateMarkup(hasFile, isUploaded, weight > 0)}</div>
            <div class="grid grid-cols-1 gap-2">
                <div>
                    <label class="block text-xs font-semibold text-gray-600 mb-1">Jenis Sampah</label>
                    <select class="input-jenis-sampah w-full rounded-xl border border-gray-200 px-3 py-2 text-sm">
                        ${JENIS_SAMPAH.map((js) => `<option value="${js}" ${js === jenisSampah ? "selected" : ""}>${js}</option>`).join("")}
                    </select>
                </div>
                <div>
                    <label class="block text-xs font-semibold text-gray-600 mb-1">Berat (kg)</label>
                    <input type="text" inputmode="decimal" class="input-berat-timbangan-display w-full rounded-xl border border-gray-200 px-3 py-2 text-sm" placeholder="Contoh: 54,45" value="${weight > 0 ? formatWeightDisplay(weight) : ""}" />
                    <input type="hidden" class="input-berat-timbangan-value" value="${weight.toFixed(2)}" />
                </div>
            </div>
        `;

    const fileInput = item.querySelector(".input-foto-timbangan");
    const previewWrap = item.querySelector(".input-preview-wrap");
    const previewImage = item.querySelector(".input-preview-image");
    const ocrInfoEl = item.querySelector(".input-ocr-info");
    const weightInputDisplay = item.querySelector(
      ".input-berat-timbangan-display",
    );
    const weightInputValue = item.querySelector(".input-berat-timbangan-value");
    const jenisSampahSelect = item.querySelector(".input-jenis-sampah");
    const removeBtn = item.querySelector(".btn-remove-timbangan");

        if (existingData && existingData.file) {
            const localUrl = URL.createObjectURL(existingData.file);
            previewImage.src = localUrl;
            previewWrap.classList.remove('hidden');
            previewImage.onload = function() { URL.revokeObjectURL(localUrl); };
        } else if (existingData && existingData.fotoFileName && existingData.fotoFileName.startsWith('/')) {
            previewImage.src = existingData.fotoFileName;
            previewWrap.classList.remove('hidden');
        }

        fileInput.addEventListener('change', async function() {
            if (fileInput.files && fileInput.files[0]) {
                const originalFile = fileInput.files[0];
                const photoNumber = Number(item.dataset.photoNumber || (Array.from(repeater.children).indexOf(item) + 1));
                const watermarkedFile = await applyWatermark(originalFile, photoNumber);

        const dataTransfer = new DataTransfer();
        dataTransfer.items.add(watermarkedFile);
        fileInput.files = dataTransfer.files;

        const localUrl = URL.createObjectURL(watermarkedFile);
        previewImage.src = localUrl;
        previewWrap.classList.remove("hidden");
        previewImage.onload = function () {
          URL.revokeObjectURL(localUrl);
        };

        await autoFillWeight(watermarkedFile, weightInputDisplay, ocrInfoEl);
        const parsed = parseWeightInput(weightInputDisplay.value);
        weightInputValue.value = parsed.toFixed(2);
        updateTpsTotalTimbangan();
        syncTimbanganToTpsData();

        const tps = tpsData[activeTpsIndex];
        const itemIndex = Array.from(repeater.children).indexOf(item);
        if (itemIndex >= 0 && tps.timbangan[itemIndex]) {
          tps.timbangan[itemIndex].uploaded = false;
          refreshTimbanganUploadState(item);
        }
      }
    });

    weightInputDisplay.addEventListener("input", function () {
      const cleaned = this.value.replace(/[^0-9.,]/g, "");
      this.value = cleaned;
      const parsed = parseWeightInput(cleaned);
      weightInputValue.value = parsed.toFixed(2);
      updateTpsTotalTimbangan();
      syncTimbanganToTpsData();
      refreshTimbanganUploadState(item);
      const form = tpsContentContainer.querySelector("form");
      if (form) refreshSubmitButtonState(form);
      scheduleAutoSave();
    });

        weightInputDisplay.addEventListener('blur', function() {
            const parsed = parseWeightInput(this.value);
            if (parsed > 0) {
                this.value = formatWeightDisplay(parsed);
                weightInputValue.value = parsed.toFixed(2);
            } else {
                this.value = '';
                weightInputValue.value = '0.00';
            }
            updateTpsTotalTimbangan();
            syncTimbanganToTpsData();
            refreshTimbanganUploadState(item);
            const form = tpsContentContainer.querySelector('form');
            if (form) refreshSubmitButtonState(form);
            scheduleAutoSave();
        });

        jenisSampahSelect.addEventListener('change', function() {
            updateTpsTotalTimbangan();
            syncTimbanganToTpsData();
            const form = tpsContentContainer.querySelector('form');
            if (form) refreshSubmitButtonState(form);
        });

        removeBtn.addEventListener('click', function() {
            item.remove();
            const form = tpsContentContainer.querySelector('form');
            const rep = form ? form.querySelector('.tps-timbangan-repeater') : null;
            if (rep) {
                renumberTimbanganItems(rep);
                if (rep.children.length === 0) createTimbanganItem(rep);
            }
            updateTpsTotalTimbangan();
            syncTimbanganToTpsData();
            if (form) refreshSubmitButtonState(form);
        });

    repeater.appendChild(item);
    refreshTimbanganUploadState(item);
    return item;
  }

  function syncTimbanganToTpsData() {
    const tps = tpsData[activeTpsIndex];
    const form = tpsContentContainer.querySelector("form");
    if (!form) return;

    const repeater = form.querySelector(".tps-timbangan-repeater");
    const items = repeater.querySelectorAll(".timbangan-item");
    const previousTimbangan = [...tps.timbangan];

        tps.timbangan = [];
        items.forEach((item, index) => {
            const fileInput = item.querySelector('.input-foto-timbangan');
            const weightValue = item.querySelector('.input-berat-timbangan-value');
            const weight = parseWeightInput(weightValue.value);
            const jenisSampah = item.querySelector('.input-jenis-sampah').value;
            const ocrInfo = item.querySelector('.input-ocr-info')?.textContent || 'OCR: belum diproses.';
            
            tps.timbangan.push({
                file: fileInput.files[0] || previousTimbangan[index]?.file || null,
                fotoFileName: previousTimbangan[index]?.fotoFileName || '',
                berat: [weight],
                jenisSampah: [jenisSampah],
                lokasiAngkut: [],
                uploaded: previousTimbangan[index]?.uploaded ?? false,
                ocrInfo
            });
        });
    }

  function buildSubmitFormData(tps) {
    const formData = new FormData();
    formData.append("NomorSpj", nomorSpj || "");
    formData.append("TpsName", tps.name || DEFAULT_TPS_NAME);
    formData.append("LokasiAngkutId", tps.lokasiAngkutId || "");
    formData.append("SpjDetailId", tps.spjDetailId || "");
    formData.append("Latitude", tps.latitude);
    formData.append("Longitude", tps.longitude);
    formData.append("AlamatJalan", tps.alamatJalan);
    formData.append("WaktuKedatangan", tps.waktuKedatangan);
    formData.append("TotalTimbangan", tps.totalTimbangan);
    formData.append("TotalOrganik", tps.totalOrganik);
    formData.append("TotalAnorganik", tps.totalAnorganik);
    formData.append("TotalResidu", tps.totalResidu);
    formData.append("NamaPetugas", tps.namaPetugas);

    tps.fotoKedatangan.forEach((file) => {
      if (isBrowserFile(file)) {
        formData.append("FotoKedatangan", file);
      }
    });
    tps.timbangan.forEach((timb) => {
      if (isBrowserFile(timb.file)) formData.append("FotoTimbangan", timb.file);
      const weight = timb.berat && timb.berat.length > 0 ? timb.berat[0] : 0;
      const jenis =
        timb.jenisSampah && timb.jenisSampah.length > 0
          ? timb.jenisSampah[0]
          : DEFAULT_JENIS;
      formData.append("BeratTimbangan", weight);
      formData.append("JenisSampahList", jenis);
    });
    tps.fotoPetugas.forEach((file) => {
      if (isBrowserFile(file)) {
        formData.append("FotoPetugas", file);
      }
    });

    const antiForgeryTokenEl = document.querySelector(
      '#upst-antiforgery input[name="__RequestVerificationToken"]',
    );
    if (antiForgeryTokenEl && antiForgeryTokenEl.value) {
      formData.append("__RequestVerificationToken", antiForgeryTokenEl.value);
    }

    return formData;
  }

    async function uploadSingleFotoTimbangan(itemIndex, targetItem = null) {
        const tps = tpsData[activeTpsIndex];
        if (!tps.timbangan[itemIndex] || !tps.timbangan[itemIndex].file) {
            showToast('Belum ada foto timbangan yang dipilih!', 'error');
            return;
        }
        const weight = tps.timbangan[itemIndex].berat && tps.timbangan[itemIndex].berat.length > 0
            ? tps.timbangan[itemIndex].berat[0]
            : 0;
        if (weight <= 0) {
            showToast('Isi berat manual dulu sebelum upload foto timbangan.', 'error');
            return;
        }

    if (!targetItem) {
      const form = tpsContentContainer.querySelector("form");
      const repeater = form
        ? form.querySelector(".tps-timbangan-repeater")
        : null;
      const items = repeater
        ? repeater.querySelectorAll(".timbangan-item")
        : [];
      targetItem = items[itemIndex] || null;
    }

        const uploadBtn = targetItem ? targetItem.querySelector('.btn-upload-timbangan') : null;
        if (uploadBtn) { uploadBtn.disabled = true; uploadBtn.textContent = 'Mengupload...'; }

        const formData = new FormData();
        formData.append('FotoTimbangan', tps.timbangan[itemIndex].file);
        formData.append('NomorSpj', nomorSpj || '');
        formData.append('NamaTps', tps.name || DEFAULT_TPS_NAME);
        formData.append('SpjDetailId', tps.spjDetailId || '');
        formData.append('LokasiAngkutId', tps.lokasiAngkutId || '');
        formData.append('ItemIndex', itemIndex);
        formData.append('JenisSampah', (tps.timbangan[itemIndex].jenisSampah && tps.timbangan[itemIndex].jenisSampah[0]) || DEFAULT_JENIS);
        formData.append('Berat', weight.toFixed(2));

        try {
            const res = await fetch('/upst/detail-penjemputan/upload-foto-timbangan-non-tps', { method: 'POST', body: formData });
            const data = await res.json();
            if (res.ok && data.success) {
                tps.timbangan[itemIndex].uploaded = true;
                tps.timbangan[itemIndex].fotoFileName = data.fileUrl || data.fileName || '';
                const fotoInputTimb = targetItem?.querySelector('.input-foto-timbangan');
                if (fotoInputTimb) fotoInputTimb.value = '';
                showToast(data.message || `Foto timbangan #${itemIndex + 1} berhasil diupload.`, 'success');
            } else {
                showToast(data.message || 'Gagal upload foto timbangan.', 'error');
                if (uploadBtn) { uploadBtn.disabled = false; uploadBtn.textContent = 'Upload Foto Timbangan Ini'; }
            }
        } catch {
            showToast('Koneksi gagal saat upload foto timbangan.', 'error');
            if (uploadBtn) { uploadBtn.disabled = false; uploadBtn.textContent = 'Upload Foto Timbangan Ini'; }
        }

        if (targetItem) refreshTimbanganUploadState(targetItem);
        syncTimbanganToTpsData();
        const form = tpsContentContainer.querySelector('form');
        if (form) refreshSubmitButtonState(form);
        scheduleAutoSave();
    }

    async function uploadFotoKedatangan() {
        const tps = tpsData[activeTpsIndex];
        if (tps.fotoKedatangan.length === 0) {
            showToast('Belum ada foto kedatangan yang dipilih!', 'error');
            return;
        }

        const form = tpsContentContainer.querySelector('form');
        const btn = form ? form.querySelector('.tps-btn-upload-kedatangan') : null;
        if (btn) { btn.disabled = true; btn.textContent = 'Mengupload...'; }

        const formData = new FormData();
        tps.fotoKedatangan.forEach(f => formData.append('FotoKedatangan', f));
        formData.append('NomorSpj', nomorSpj || '');
        formData.append('NamaTps', tps.name || DEFAULT_TPS_NAME);
        formData.append('SpjDetailId', tps.spjDetailId || '');
        formData.append('LokasiAngkutId', tps.lokasiAngkutId || '');
        formData.append('WaktuKedatangan', tps.waktuKedatangan || '');
        formData.append('Latitude', tps.latitude || '');
        formData.append('Longitude', tps.longitude || '');
        formData.append('AlamatJalan', tps.alamatJalan || '');

        try {
            const res = await fetch('/upst/detail-penjemputan/upload-foto-kedatangan-non-tps', { method: 'POST', body: formData });
            const data = await res.json();
            if (res.ok && data.success) {
                tps.fotoKedatanganUploaded = true;
                tps.fotoKedatanganFileNames = data.fileUrls || data.fileNames || [];
                showToast(data.message || 'Foto kedatangan berhasil diupload.', 'success');
                if (form) {
                    const fotoInput = form.querySelector('.tps-foto-kedatangan');
                    if (fotoInput) fotoInput.value = '';
                    refreshKedatanganUploadState(form);
                }
                scheduleAutoSave();
            } else {
                showToast(data.message || 'Gagal upload foto kedatangan.', 'error');
                if (btn) { btn.disabled = false; btn.textContent = `Upload ${tps.fotoKedatangan.length} Foto Kedatangan`; }
            }
        } catch {
            showToast('Koneksi gagal saat upload foto kedatangan.', 'error');
            if (btn) { btn.disabled = false; btn.textContent = `Upload ${tps.fotoKedatangan.length} Foto Kedatangan`; }
        }
    }

    async function uploadFotoPetugas() {
        const tps = tpsData[activeTpsIndex];
        if (tps.fotoPetugas.length === 0) {
            showToast('Belum ada foto petugas yang dipilih!', 'error');
            return;
        }
        if (!tps.namaPetugas.trim()) {
            showToast('Nama petugas wajib diisi sebelum upload foto petugas!', 'error');
            return;
        }

        const form = tpsContentContainer.querySelector('form');
        const btn = form ? form.querySelector('.tps-btn-upload-petugas:not([disabled])') : null;
        if (btn) { btn.disabled = true; btn.textContent = 'Mengupload...'; }

        const formData = new FormData();
        tps.fotoPetugas.forEach(f => formData.append('FotoPetugas', f));
        formData.append('NomorSpj', nomorSpj || '');
        formData.append('NamaTps', tps.name || DEFAULT_TPS_NAME);
        formData.append('SpjDetailId', tps.spjDetailId || '');
        formData.append('LokasiAngkutId', tps.lokasiAngkutId || '');
        formData.append('NamaPetugas', tps.namaPetugas);

        try {
            const res = await fetch('/upst/detail-penjemputan/upload-foto-petugas-non-tps', { method: 'POST', body: formData });
            const data = await res.json();
            if (res.ok && data.success) {
                tps.fotoPetugasUploaded = true;
                tps.fotoPetugasFileNames = data.fileUrls || data.fileNames || [];
                showToast(data.message || 'Foto petugas berhasil diupload.', 'success');
                if (form) {
                    const fotoInput = form.querySelector('.tps-foto-petugas');
                    if (fotoInput) fotoInput.value = '';
                    refreshPetugasUploadState(form);
                }
                scheduleAutoSave();
            } else {
                showToast(data.message || 'Gagal upload foto petugas.', 'error');
                if (btn) { btn.disabled = false; btn.textContent = `Upload ${tps.fotoPetugas.length} Foto Petugas`; }
            }
        } catch {
            showToast('Koneksi gagal saat upload foto petugas.', 'error');
            if (btn) { btn.disabled = false; btn.textContent = `Upload ${tps.fotoPetugas.length} Foto Petugas`; }
        }
    }

    async function submitTpsData() {
        const tps = tpsData[activeTpsIndex];
        const submitState = getSubmitState(tps);
        if (!submitState.canSubmit) { showToast(submitState.message, 'error'); return; }

        const form = tpsContentContainer.querySelector('form');
        const submitBtn = form ? form.querySelector('button[type="submit"]') : null;
        if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Menyimpan...'; }

        const formData = buildSubmitFormData(tps);

        try {
          const res = await fetch('/upst/detail-penjemputan', {
            method: 'POST',
            headers: {
              'X-Requested-With': 'XMLHttpRequest',
              'Accept': 'application/json'
            },
            body: formData
          });
          const result = await res.json().catch(() => null);

          if (res.ok && result?.success) {
                tps.submitted = true;
            showToast(result.message || 'Data berhasil disimpan!', 'success');
                setTimeout(() => { window.location.href = '/upst/detail-penjemputan/detail-selesai-tanpa-tps'; }, 1500);
            } else {
            showToast(result?.message || 'Gagal submit data.', 'error');
                if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Submit'; }
            }
        } catch {
            showToast('Koneksi gagal saat submit.', 'error');
            if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Submit'; }
        }
    }

    function showToast(message, type = 'info') {
        let container = document.getElementById('espj-toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'espj-toast-container';
            container.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);z-index:9999;display:flex;flex-direction:column;align-items:center;gap:8px;pointer-events:none;';
            document.body.appendChild(container);
        }
        const toast = document.createElement('div');
        const bg = type === 'success' ? '#16a34a' : type === 'error' ? '#dc2626' : '#2563eb';
        toast.style.cssText = `background:${bg};color:#fff;padding:10px 18px;border-radius:16px;font-size:13px;font-weight:600;box-shadow:0 4px 24px rgba(0,0,0,.18);opacity:0;transition:opacity .25s;max-width:320px;text-align:center;`;
        toast.textContent = message;
        container.appendChild(toast);
        requestAnimationFrame(() => { toast.style.opacity = '1'; });
        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => toast.remove(), 300);
        }, 3200);
    }

  const nomorSpjEl = document.querySelector(".text-gray-600.font-mono");
  if (nomorSpjEl && nomorSpjEl.textContent) {
    nomorSpj = nomorSpjEl.textContent.trim();
  }

    showLoadingOverlay();
    try {
      initializeLocation();
      await loadDetailData();
      await loadRecordForCurrentSpj();
      renderTpsForm();
    } finally {
      hideLoadingOverlay();
    }

    function renderServerImagePreview(fileUrls, container) {
        container.innerHTML = '';
        if (!fileUrls || fileUrls.length === 0) return;
        container.className = 'space-y-2';
        fileUrls.forEach((url, index) => {
            const item = document.createElement('div');
            item.className = 'rounded-xl border border-gray-200 overflow-hidden bg-black';
            const isUrl = typeof url === 'string' && (url.startsWith('/') || url.startsWith('http'));
            if (isUrl) {
                item.innerHTML = `
                    <div class="h-44 bg-black/80">
                        <img src="${url}" alt="Foto ${index + 1}" class="w-full h-full object-contain" loading="lazy" />
                    </div>
                `;
            } else {
                item.className = 'rounded-xl border border-green-200 bg-green-50 h-44 flex items-center justify-center';
                item.innerHTML = `<p class="text-xs text-green-700 font-semibold px-3">&#10003; Foto ${index + 1}</p>`;
            }
            container.appendChild(item);
        });
    }
});
