// RAPBS annual planning: categories, budget items, COA mapping, and yearly amounts.
const { useState: useState$rapbs, useMemo: useMemo$rapbs, useEffect: useEffect$rapbs, useRef: useRef$rapbs } = React;

const RAPBS_TYPE_META = {
  income: {
    label: 'Pendapatan',
    itemLabel: 'Target Pemasukan',
    description: 'Rencana penerimaan sekolah',
    allowedAccountTypes: ['pendapatan', 'liabilitas'],
  },
  outcome: {
    label: 'Belanja',
    itemLabel: 'Anggaran Belanja',
    description: 'Rencana penggunaan dana sekolah',
    allowedAccountTypes: ['beban', 'aset', 'liabilitas'],
  },
};

const RAPBS_APPROVAL_META = {
  draft: { label: 'Draft', tone: 'neutral' },
  pending: { label: 'Menunggu Persetujuan', tone: 'warning' },
  approved: { label: 'Disetujui', tone: 'success' },
  rejected: { label: 'Ditolak', tone: 'danger' },
};

const RAPBS_HISTORY_LABELS = {
  submitted: 'Diajukan untuk persetujuan',
  approved: 'RAPBS disetujui',
  rejected: 'RAPBS ditolak',
};

const RAPBS_TEMPLATE_KIND = 'rapbs-budget';
const RAPBS_TEMPLATE_VERSION = '1';
const RAPBS_TEMPLATE_HEADERS = ['ID Item', 'Jenis', 'Kategori', 'Budget Item', 'Deskripsi', 'Kode COA', 'Nama COA', 'Status', 'Nominal Anggaran'];

function downloadRapbsTemplate({ academicYear, categories, items, budgets }) {
  if (!window.XLSX) throw new Error('Library Excel belum tersedia. Muat ulang halaman lalu coba lagi.');
  const categoryById = Object.fromEntries(categories.map(category => [category.id, category]));
  const amountByItemId = Object.fromEntries(budgets.filter(budget => budget.year === academicYear.budgetYear).map(budget => [budget.itemId, budget.amount]));
  const rows = [...items]
    .sort((a, b) => {
      if (a.type !== b.type) return a.type === 'income' ? -1 : 1;
      const categoryOrder = (categoryById[a.categoryId]?.sortOrder || 0) - (categoryById[b.categoryId]?.sortOrder || 0);
      return categoryOrder || a.name.localeCompare(b.name, 'id');
    })
    .map(item => ({
      'ID Item': item.id,
      Jenis: RAPBS_TYPE_META[item.type].label,
      Kategori: categoryById[item.categoryId]?.name || '',
      'Budget Item': item.name,
      Deskripsi: item.description || '',
      'Kode COA': item.coaCode,
      'Nama COA': ACC[item.coaCode]?.name || '',
      Status: item.active ? 'Aktif' : 'Nonaktif',
      'Nominal Anggaran': amountByItemId[item.id] || 0,
    }));
  const budgetSheet = XLSX.utils.json_to_sheet(rows, { header: RAPBS_TEMPLATE_HEADERS });
  budgetSheet['!cols'] = [
    { wch: 19 }, { wch: 14 }, { wch: 28 }, { wch: 32 }, { wch: 42 },
    { wch: 13 }, { wch: 30 }, { wch: 13 }, { wch: 20 },
  ];
  budgetSheet['!autofilter'] = { ref: budgetSheet['!ref'] };
  for (let row = 2; row <= rows.length + 1; row += 1) {
    const amountCell = budgetSheet[`I${row}`];
    if (amountCell) amountCell.z = '#,##0';
  }

  const metadataSheet = XLSX.utils.aoa_to_sheet([
    ['Kunci', 'Nilai'],
    ['template', RAPBS_TEMPLATE_KIND],
    ['version', RAPBS_TEMPLATE_VERSION],
    ['academicYearId', academicYear.id],
    ['academicYearName', academicYear.name],
    ['budgetYear', String(academicYear.budgetYear)],
  ]);
  const workbook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(workbook, budgetSheet, 'RAPBS');
  XLSX.utils.book_append_sheet(workbook, metadataSheet, '_Metadata');
  workbook.Workbook = { Sheets: [{ Hidden: 0 }, { Hidden: 2 }] };
  XLSX.writeFile(workbook, `Template_RAPBS_${academicYear.name.replace('/', '-')}.xlsx`);
}

function parseRapbsAmount(value) {
  if (value === '' || value === null || value === undefined) return { empty: true };
  if (typeof value === 'number') {
    return Number.isSafeInteger(value) && value >= 0 ? { value } : { error: 'Nominal harus berupa bilangan bulat positif atau nol.' };
  }
  const text = String(value).trim().replace(/^Rp\s*/i, '').replace(/\s/g, '');
  if (!text) return { empty: true };
  if (!/^\d+$/.test(text) && !/^\d{1,3}([.,]\d{3})+$/.test(text)) {
    return { error: 'Gunakan bilangan bulat, misalnya 1500000 atau 1.500.000.' };
  }
  const amount = Number(text.replace(/[.,]/g, ''));
  return Number.isSafeInteger(amount) ? { value: amount } : { error: 'Nominal terlalu besar.' };
}

function readRapbsImportFile(file, { academicYear, categories, items, budgets }) {
  return file.arrayBuffer().then(buffer => {
    if (!window.XLSX) throw new Error('Library Excel belum tersedia. Muat ulang halaman lalu coba lagi.');
    const workbook = XLSX.read(buffer, { type: 'array' });
    const budgetSheet = workbook.Sheets.RAPBS;
    const metadataSheet = workbook.Sheets._Metadata;
    if (!budgetSheet || !metadataSheet) throw new Error('File bukan template RAPBS yang valid. Unduh template terbaru dari halaman ini.');

    const metadata = Object.fromEntries(XLSX.utils.sheet_to_json(metadataSheet, { header: 1, defval: '' }).slice(1).map(row => [String(row[0]), String(row[1])]));
    if (metadata.template !== RAPBS_TEMPLATE_KIND || metadata.version !== RAPBS_TEMPLATE_VERSION) {
      throw new Error('Versi template tidak sesuai. Unduh template RAPBS terbaru lalu coba lagi.');
    }
    if (metadata.academicYearId !== academicYear.id || metadata.budgetYear !== String(academicYear.budgetYear)) {
      throw new Error(`Template ini bukan untuk Tahun Ajaran ${academicYear.name}.`);
    }

    const sheetRows = XLSX.utils.sheet_to_json(budgetSheet, { header: 1, defval: '' });
    const headers = (sheetRows[0] || []).map(value => String(value).trim());
    const missingHeaders = RAPBS_TEMPLATE_HEADERS.filter(header => !headers.includes(header));
    if (missingHeaders.length) throw new Error(`Kolom template tidak lengkap: ${missingHeaders.join(', ')}.`);

    const rawRows = XLSX.utils.sheet_to_json(budgetSheet, { defval: '' });
    const itemById = Object.fromEntries(items.map(item => [item.id, item]));
    const categoryById = Object.fromEntries(categories.map(category => [category.id, category]));
    const currentAmountByItemId = Object.fromEntries(budgets.filter(budget => budget.year === academicYear.budgetYear).map(budget => [budget.itemId, budget.amount]));
    const idCounts = rawRows.reduce((counts, row) => {
      const id = String(row['ID Item']).trim();
      if (id) counts[id] = (counts[id] || 0) + 1;
      return counts;
    }, {});
    const includedIds = new Set();

    const reviewRows = rawRows.map((row, index) => {
      const itemId = String(row['ID Item']).trim();
      const item = itemById[itemId];
      const rowNumber = index + 2;
      if (!item) {
        return { rowNumber, itemId, itemName: itemId || 'ID Item kosong', currentAmount: null, importedAmount: null, delta: null, status: 'error', message: 'Item tidak dikenal atau tidak termasuk RAPBS tahun ajaran ini.' };
      }
      includedIds.add(itemId);
      const currentAmount = currentAmountByItemId[itemId] || 0;
      const expected = {
        Jenis: RAPBS_TYPE_META[item.type].label,
        Kategori: categoryById[item.categoryId]?.name || '',
        'Budget Item': item.name,
        Deskripsi: item.description || '',
        'Kode COA': item.coaCode,
        'Nama COA': ACC[item.coaCode]?.name || '',
        Status: item.active ? 'Aktif' : 'Nonaktif',
      };
      const changedReferences = Object.entries(expected).filter(([key, value]) => String(row[key]).trim() !== String(value).trim()).map(([key]) => key);
      const parsedAmount = parseRapbsAmount(row['Nominal Anggaran']);
      const errors = [];
      if (idCounts[itemId] > 1) errors.push('ID Item duplikat.');
      if (changedReferences.length) errors.push(`Kolom referensi berubah: ${changedReferences.join(', ')}.`);
      if (parsedAmount.error) errors.push(parsedAmount.error);
      if (errors.length) {
        return { rowNumber, itemId, itemName: item.name, currentAmount, importedAmount: parsedAmount.value ?? null, delta: null, status: 'error', message: errors.join(' ') };
      }
      if (parsedAmount.empty) {
        return { rowNumber, itemId, itemName: item.name, currentAmount, importedAmount: currentAmount, delta: 0, status: 'unchanged', message: 'Nominal kosong; nilai lama dipertahankan.' };
      }
      const delta = parsedAmount.value - currentAmount;
      return { rowNumber, itemId, itemName: item.name, currentAmount, importedAmount: parsedAmount.value, delta, status: delta === 0 ? 'unchanged' : 'changed', message: delta === 0 ? 'Tidak ada perubahan.' : 'Siap diperbarui.' };
    });

    items.filter(item => !includedIds.has(item.id)).forEach(item => {
      const currentAmount = currentAmountByItemId[item.id] || 0;
      reviewRows.push({ rowNumber: null, itemId: item.id, itemName: item.name, currentAmount, importedAmount: currentAmount, delta: 0, status: 'unchanged', message: 'Baris tidak ada di file; nilai lama dipertahankan.' });
    });
    return reviewRows;
  });
}

function RapbsSelect({ value, options, placeholder, disabled, onChange }) {
  const [open, setOpen] = useState$rapbs(false);
  const ref = useRef$rapbs(null);
  const selected = options.find(option => option.value === value);

  useEffect$rapbs(() => {
    if (!open) return;
    const close = event => { if (!ref.current?.contains(event.target)) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  return (
    <div className={'rapbs-select' + (disabled ? ' disabled' : '')} ref={ref}>
      <button
        type="button"
        className={'rapbs-select-trigger' + (open ? ' open' : '')}
        disabled={disabled}
        aria-haspopup="listbox"
        aria-expanded={open}
        onClick={() => setOpen(current => !current)}
      >
        <span className={selected ? '' : 'muted'}>{selected?.label || placeholder}</span>
        <Icon name="chevronDown" size={15} />
      </button>
      {open && (
        <div className="rapbs-select-menu" role="listbox">
          {options.length === 0 && <div className="rapbs-select-empty">Tidak ada pilihan tersedia.</div>}
          {options.map(option => (
            <button
              type="button"
              role="option"
              aria-selected={option.value === value}
              className={'rapbs-select-option' + (option.value === value ? ' selected' : '')}
              key={option.value}
              onClick={() => { onChange(option.value); setOpen(false); }}
            >
              <span>
                <span className="rapbs-select-option-label">{option.label}</span>
                {option.hint && <span className="rapbs-select-option-hint">{option.hint}</span>}
              </span>
              {option.value === value && <Icon name="check" size={14} />}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function RapbsDialogShell({ title, description, onClose, children, footer, className = '', backdropClassName = '' }) {
  useEffect$rapbs(() => {
    const close = event => { if (event.key === 'Escape') onClose(); };
    window.addEventListener('keydown', close);
    return () => window.removeEventListener('keydown', close);
  }, [onClose]);

  return (
    <div className={'dialog-backdrop ' + backdropClassName} onMouseDown={onClose}>
      <div className={'dialog rapbs-dialog ' + className} onMouseDown={event => event.stopPropagation()}>
        <div className="dialog-header rapbs-dialog-header">
          <div>
            <h2 className="dialog-title">{title}</h2>
            {description && <p className="dialog-desc">{description}</p>}
          </div>
          <button type="button" className="icon-button" aria-label="Tutup" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        {children}
        {footer && <div className="dialog-footer rapbs-dialog-footer">{footer}</div>}
      </div>
    </div>
  );
}

function CategoryDialog({ categories, includedCategoryIds, academicYearName, onClose, onAdd, onInclude, onRemove, canManage }) {
  const [name, setName] = useState$rapbs('');
  const [description, setDescription] = useState$rapbs('');
  const [error, setError] = useState$rapbs('');

  const submit = event => {
    event.preventDefault();
    const cleanName = name.trim();
    if (!cleanName) { setError('Nama kategori wajib diisi.'); return; }
    if (categories.some(category => category.name.toLowerCase() === cleanName.toLowerCase())) {
      setError('Kategori dengan nama tersebut sudah ada.');
      return;
    }
    onAdd({ name: cleanName, description: description.trim() });
    setName('');
    setDescription('');
    setError('');
  };

  return (
    <RapbsDialogShell title="Kategori RAPBS" description={`Atur kategori yang digunakan pada Tahun Ajaran ${academicYearName}.`} onClose={onClose} className="rapbs-category-dialog">
      <div className="dialog-body rapbs-category-body">
        <div className="rapbs-category-list">
          {[...categories].sort((a, b) => a.sortOrder - b.sortOrder).map((category, index) => {
            const included = includedCategoryIds.includes(category.id);
            return <div className="rapbs-category-row" key={category.id}>
              <span className="rapbs-category-order">{String(index + 1).padStart(2, '0')}</span>
              <span className="rapbs-category-copy">
                <strong>{category.name}</strong>
                <small>{category.description || 'Tanpa deskripsi'}</small>
              </span>
              <Pill tone={included ? 'ok' : 'neutral'}>{included ? 'Digunakan' : 'Tidak digunakan'}</Pill>
              {canManage && (included
                ? <Btn tone="ghost" size="sm" onClick={() => onRemove(category)}>Hapus dari TA</Btn>
                : <Btn tone="outline" size="sm" icon="plus" onClick={() => onInclude(category)}>Tambahkan</Btn>)}
            </div>
          })}
        </div>
        {canManage && (
          <form className="rapbs-category-form" onSubmit={submit}>
            <div className="rapbs-form-section-title">Tambah Kategori</div>
            <div className="field">
              <label htmlFor="rapbs-category-name">Nama Kategori</label>
              <input id="rapbs-category-name" className={'input' + (error ? ' invalid' : '')} placeholder="Contoh: Belanja Perpustakaan" value={name} onChange={event => { setName(event.target.value); setError(''); }} />
              {error && <span className="field-error">{error}</span>}
            </div>
            <div className="field">
              <label htmlFor="rapbs-category-description">Deskripsi <span className="muted">(opsional)</span></label>
              <input id="rapbs-category-description" className="input" placeholder="Tujuan kategori" value={description} onChange={event => setDescription(event.target.value)} />
            </div>
            <Btn type="submit" tone="primary" icon="plus">Tambah Kategori</Btn>
          </form>
        )}
      </div>
    </RapbsDialogShell>
  );
}

function AcademicYearDialog({ academicYears, suggestedStartYear, onClose, onCreate }) {
  const [startYear, setStartYear] = useState$rapbs(String(suggestedStartYear));
  const [error, setError] = useState$rapbs('');
  const parsedStartYear = Number(startYear);
  const endYear = Number.isInteger(parsedStartYear) ? parsedStartYear + 1 : null;
  const name = endYear ? `${parsedStartYear}/${endYear}` : '—';

  useEffect$rapbs(() => {
    const close = event => { if (event.key === 'Escape') onClose(); };
    window.addEventListener('keydown', close);
    return () => window.removeEventListener('keydown', close);
  }, [onClose]);

  const submit = event => {
    event.preventDefault();
    if (!Number.isInteger(parsedStartYear) || parsedStartYear < 2000 || parsedStartYear > 2099) {
      setError('Masukkan tahun mulai antara 2000 dan 2099.');
      return;
    }
    if (academicYears.some(academicYear => academicYear.startYear === parsedStartYear)) {
      setError(`Tahun Ajaran ${name} sudah tersedia.`);
      return;
    }
    onCreate({
      id: `ay-${parsedStartYear}-${endYear}`,
      name,
      startYear: parsedStartYear,
      endYear,
      budgetYear: endYear,
      status: 'draft',
      approvalHistory: [],
    });
  };

  return (
    <div className="dialog-backdrop" onMouseDown={onClose}>
      <form className="dialog rapbs-dialog rapbs-academic-year-dialog" onSubmit={submit} onMouseDown={event => event.stopPropagation()}>
        <div className="dialog-header rapbs-dialog-header">
          <div>
            <h2 className="dialog-title">Buat Tahun Ajaran</h2>
            <p className="dialog-desc">Tahun Ajaran baru dimulai dengan nominal anggaran kosong.</p>
          </div>
          <button type="button" className="icon-button" aria-label="Tutup" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="dialog-body rapbs-academic-year-form">
          <div className="rapbs-academic-year-preview">
            <span>Tahun Ajaran</span>
            <strong>{name}</strong>
            <small>Tahun anggaran: {endYear || '—'}</small>
          </div>
          <div className="field">
            <label htmlFor="rapbs-start-year">Tahun Mulai</label>
            <input id="rapbs-start-year" className={'input' + (error ? ' invalid' : '')} type="number" min="2000" max="2099" value={startYear} onChange={event => { setStartYear(event.target.value); setError(''); }} />
            {error ? <span className="field-error">{error}</span> : <span className="field-help">Tahun selesai akan dibuat otomatis satu tahun setelah tahun mulai.</span>}
          </div>
        </div>
        <div className="dialog-footer rapbs-dialog-footer">
          <Btn type="button" onClick={onClose}>Batal</Btn>
          <Btn type="submit" tone="primary">Buat dan Buka</Btn>
        </div>
      </form>
    </div>
  );
}

function ReuseItemDialog({ items, academicYearName, onClose, onReuse }) {
  const [itemId, setItemId] = useState$rapbs('');
  const [amount, setAmount] = useState$rapbs('');
  const [errors, setErrors] = useState$rapbs({});
  const selectedItem = items.find(item => item.id === itemId);
  const itemOptions = items.map(item => ({
    value: item.id,
    label: item.name,
    hint: `${RAPBS_TYPE_META[item.type].label} · ${item.coaCode} · ${ACC[item.coaCode]?.name}`,
  }));

  const submit = event => {
    event.preventDefault();
    const nextErrors = {};
    const parsedAmount = Number(amount.replace(/\D/g, ''));
    if (!itemId) nextErrors.itemId = 'Pilih Budget Item.';
    if (!amount) nextErrors.amount = 'Nominal anggaran wajib diisi.';
    if (Object.keys(nextErrors).length) { setErrors(nextErrors); return; }
    onReuse(selectedItem, parsedAmount);
  };

  return (
    <div className="dialog-backdrop" onMouseDown={onClose}>
      <form className="dialog rapbs-dialog rapbs-reuse-dialog" onSubmit={submit} onMouseDown={event => event.stopPropagation()}>
        <div className="dialog-header rapbs-dialog-header">
          <div><h2 className="dialog-title">Gunakan Item Lama</h2><p className="dialog-desc">Tambahkan kembali item master ke Tahun Ajaran {academicYearName}.</p></div>
          <button type="button" className="icon-button" aria-label="Tutup" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="dialog-body rapbs-item-form">
          <div className="field">
            <label>Budget Item</label>
            <RapbsSelect value={itemId} options={itemOptions} placeholder="Pilih item dari Tahun Ajaran lain..." onChange={value => { setItemId(value); setErrors(current => ({ ...current, itemId: '' })); }} />
            {errors.itemId && <span className="field-error">{errors.itemId}</span>}
          </div>
          {selectedItem && (
            <div className="rapbs-reuse-item-detail">
              <Pill tone={selectedItem.type === 'income' ? 'ok' : 'warning'}>{RAPBS_TYPE_META[selectedItem.type].label}</Pill>
              <span className="code">{selectedItem.coaCode}</span>
              <span>{ACC[selectedItem.coaCode]?.name}</span>
            </div>
          )}
          <div className="field">
            <label htmlFor="rapbs-reuse-amount">Nominal Anggaran · {academicYearName}</label>
            <div className={'currency-input' + (errors.amount ? ' invalid' : '')}>
              <span>Rp</span>
              <input id="rapbs-reuse-amount" inputMode="numeric" placeholder="0" value={amount ? Number(amount).toLocaleString('id-ID') : ''} onChange={event => { setAmount(event.target.value.replace(/\D/g, '')); setErrors(current => ({ ...current, amount: '' })); }} />
            </div>
            {errors.amount && <span className="field-error">{errors.amount}</span>}
          </div>
        </div>
        <div className="dialog-footer rapbs-dialog-footer"><Btn type="button" onClick={onClose}>Batal</Btn><Btn type="submit" tone="primary">Tambahkan ke Tahun Ajaran</Btn></div>
      </form>
    </div>
  );
}

function RemoveFromYearDialog({ target, academicYearName, onClose, onConfirm }) {
  const isCategory = target.kind === 'category';
  return (
    <RapbsDialogShell
      title={isCategory ? 'Hapus Kategori dari Tahun Ajaran?' : 'Hapus Item dari Tahun Ajaran?'}
      description="Data master dan Tahun Ajaran lain tidak akan terpengaruh."
      onClose={onClose}
      className="rapbs-confirm-dialog"
      footer={<><Btn onClick={onClose}>Batal</Btn><Btn tone="destructive" onClick={onConfirm}>Hapus dari {academicYearName}</Btn></>}
    >
      <div className="dialog-body rapbs-confirm-body">
        <span className="rapbs-confirm-icon"><Icon name="trash" size={20} /></span>
        <div><strong>{target.value.name}</strong><p>{isCategory ? 'Seluruh Budget Item dalam kategori ini juga dikeluarkan dari Tahun Ajaran aktif dan nominal tahun ini dihapus.' : 'Nominal item pada Tahun Ajaran aktif akan dihapus. Item dapat digunakan kembali nanti.'}</p></div>
      </div>
    </RapbsDialogShell>
  );
}

function RejectRapbsDialog({ academicYearName, onClose, onReject }) {
  const [reason, setReason] = useState$rapbs('');
  const [error, setError] = useState$rapbs('');
  const submit = event => {
    event.preventDefault();
    if (!reason.trim()) { setError('Alasan penolakan wajib diisi.'); return; }
    onReject(reason.trim());
  };
  return (
    <div className="dialog-backdrop" onMouseDown={onClose}>
      <form className="dialog rapbs-dialog rapbs-reject-dialog" onSubmit={submit} onMouseDown={event => event.stopPropagation()}>
        <div className="dialog-header rapbs-dialog-header">
          <div><h2 className="dialog-title">Tolak RAPBS {academicYearName}</h2><p className="dialog-desc">Berikan catatan yang dapat ditindaklanjuti oleh Bendahara.</p></div>
          <button type="button" className="icon-button" aria-label="Tutup" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="dialog-body">
          <div className="field"><label htmlFor="rapbs-reject-reason">Alasan Penolakan</label><textarea id="rapbs-reject-reason" className={'textarea' + (error ? ' invalid' : '')} rows="4" placeholder="Jelaskan bagian yang perlu diperbaiki..." value={reason} onChange={event => { setReason(event.target.value); setError(''); }} />{error && <span className="field-error">{error}</span>}</div>
        </div>
        <div className="dialog-footer rapbs-dialog-footer"><Btn type="button" onClick={onClose}>Batal</Btn><Btn type="submit" tone="destructive">Tolak RAPBS</Btn></div>
      </form>
    </div>
  );
}

function ApprovalHistoryDialog({ academicYear, onClose }) {
  return (
    <RapbsDialogShell title="Riwayat Persetujuan" description={`RAPBS Tahun Ajaran ${academicYear.name}`} onClose={onClose} className="rapbs-history-dialog">
      <div className="dialog-body rapbs-history-list">
        {academicYear.approvalHistory.length === 0 && <div className="empty">Belum ada aktivitas persetujuan.</div>}
        {[...academicYear.approvalHistory].reverse().map(event => (
          <div className={'rapbs-history-event ' + event.action} key={event.id}>
            <span className="rapbs-history-marker"><Icon name={event.action === 'approved' ? 'check' : event.action === 'rejected' ? 'x' : 'arrowRight'} size={14} /></span>
            <div><div className="rapbs-history-event-head"><strong>{RAPBS_HISTORY_LABELS[event.action]}</strong><time>{new Date(event.at).toLocaleString('id-ID', { dateStyle: 'medium', timeStyle: 'short' })}</time></div><span>{event.actor} · {event.role}</span>{event.note && <p>{event.note}</p>}</div>
          </div>
        ))}
      </div>
    </RapbsDialogShell>
  );
}

function RapbsImportDialog({ academicYear, categories, items, budgets, onClose, onDownload, onImport }) {
  const inputRef = useRef$rapbs(null);
  const [fileName, setFileName] = useState$rapbs('');
  const [reviewRows, setReviewRows] = useState$rapbs([]);
  const [fileError, setFileError] = useState$rapbs('');
  const [parsing, setParsing] = useState$rapbs(false);
  const summary = useMemo$rapbs(() => reviewRows.reduce((result, row) => {
    result[row.status] += 1;
    return result;
  }, { changed: 0, unchanged: 0, error: 0 }), [reviewRows]);

  const selectFile = async file => {
    if (!file) return;
    setFileName(file.name);
    setReviewRows([]);
    setFileError('');
    if (!file.name.toLowerCase().endsWith('.xlsx')) {
      setFileError('Pilih file Excel dengan format .xlsx.');
      return;
    }
    if (file.size > 5 * 1024 * 1024) {
      setFileError('Ukuran file maksimal 5 MB.');
      return;
    }
    setParsing(true);
    try {
      const rows = await readRapbsImportFile(file, { academicYear, categories, items, budgets });
      setReviewRows(rows);
    } catch (error) {
      setFileError(error.message || 'File tidak dapat dibaca.');
    } finally {
      setParsing(false);
    }
  };

  const confirmImport = () => {
    if (summary.error || !summary.changed) return;
    onImport(reviewRows.filter(row => row.status === 'changed').map(row => ({ itemId: row.itemId, amount: row.importedAmount })));
  };

  return (
    <RapbsDialogShell
      title="Impor Anggaran RAPBS"
      description={`Perbarui nominal Tahun Ajaran ${academicYear.name} dari template Excel.`}
      onClose={onClose}
      className="rapbs-import-dialog"
      backdropClassName="rapbs-import-backdrop"
      footer={<><Btn onClick={onClose}>Batal</Btn><Btn tone="primary" icon="upload" disabled={parsing || summary.error > 0 || summary.changed === 0} onClick={confirmImport}>Impor {summary.changed || ''} Perubahan</Btn></>}
    >
      <div className="dialog-body rapbs-import-body">
        <div className="rapbs-import-guidance">
          <Icon name="alert" size={17} />
          <span>Hanya kolom <strong>Nominal Anggaran</strong> yang boleh diubah. Baris yang tidak ada atau nominal kosong akan mempertahankan nilai lama.</span>
        </div>
        <input ref={inputRef} type="file" accept=".xlsx" hidden onChange={event => { selectFile(event.target.files?.[0]); event.target.value = ''; }} />
        <div
          className={'rapbs-import-dropzone' + (fileError ? ' invalid' : '')}
          onDragOver={event => event.preventDefault()}
          onDrop={event => { event.preventDefault(); selectFile(event.dataTransfer.files?.[0]); }}
        >
          <span className="rapbs-import-file-icon"><Icon name={parsing ? 'clock' : 'upload'} size={22} /></span>
          <div><strong>{parsing ? 'Membaca file...' : fileName || 'Pilih template RAPBS'}</strong><small>Tarik file ke sini atau pilih file .xlsx, maksimal 5 MB.</small></div>
          <Btn size="sm" disabled={parsing} onClick={() => inputRef.current?.click()}>{fileName ? 'Ganti File' : 'Pilih File'}</Btn>
        </div>
        <button type="button" className="rapbs-import-template-link" onClick={onDownload}><Icon name="download" size={14} /> Unduh template terbaru untuk TA {academicYear.name}</button>
        {fileError && <div className="rapbs-import-error"><Icon name="alert" size={16} /><span>{fileError}</span></div>}

        {reviewRows.length > 0 && (
          <>
            <div className="rapbs-import-summary">
              <span><strong>{reviewRows.length}</strong> item diperiksa</span>
              <span className="changed"><strong>{summary.changed}</strong> berubah</span>
              <span><strong>{summary.unchanged}</strong> dipertahankan</span>
              {summary.error > 0 && <span className="error"><strong>{summary.error}</strong> error</span>}
            </div>
            {summary.error > 0 && <div className="rapbs-import-error"><Icon name="alert" size={16} /><span>Perbaiki semua error di file sebelum mengimpor. Tidak ada data yang akan diperbarui sebagian.</span></div>}
            <div className="table-wrap rapbs-import-table-wrap">
              <table className="data-table rapbs-import-table">
                <thead><tr><th>Baris</th><th>Budget Item</th><th className="num">Nominal Lama</th><th className="num">Nominal Baru</th><th>Status</th></tr></thead>
                <tbody>
                  {reviewRows.map((row, index) => (
                    <tr className={'rapbs-import-row ' + row.status} key={`${row.itemId}-${row.rowNumber || 'missing'}-${index}`}>
                      <td className="code">{row.rowNumber || '—'}</td>
                      <td><strong>{row.itemName}</strong><small>{row.itemId || 'Tanpa ID'} · {row.message}</small></td>
                      <td className="num">{row.currentAmount === null ? '—' : rupiah(row.currentAmount, { sym: true })}</td>
                      <td className="num">{row.importedAmount === null ? '—' : rupiah(row.importedAmount, { sym: true })}</td>
                      <td><Pill tone={row.status === 'changed' ? 'warning' : row.status === 'error' ? 'danger' : 'neutral'}>{row.status === 'changed' ? 'Berubah' : row.status === 'error' ? 'Error' : 'Tetap'}</Pill></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </>
        )}
      </div>
    </RapbsDialogShell>
  );
}

function BudgetItemDialog({ item, year, academicYearName, amount, categories, items, onClose, onSave }) {
  const editing = Boolean(item);
  const [form, setForm] = useState$rapbs(() => ({
    categoryId: item?.categoryId || '',
    name: item?.name || '',
    description: item?.description || '',
    type: item?.type || '',
    coaCode: item?.coaCode || '',
    amount: amount ? String(amount) : '',
    active: item?.active ?? true,
  }));
  const [errors, setErrors] = useState$rapbs({});

  const usedCoa = new Set(items.filter(existing => existing.id !== item?.id).map(existing => existing.coaCode));
  const eligibleAccounts = form.type
    ? ACCOUNTS
        .filter(account => RAPBS_TYPE_META[form.type].allowedAccountTypes.includes(account.type))
        .filter(account => form.type !== 'outcome' || account.type !== 'aset' || (account.group === 'Aset Tetap' && !account.contra))
        .filter(account => !usedCoa.has(account.code))
        .sort((a, b) => a.code.localeCompare(b.code))
    : [];

  const categoryOptions = [...categories]
    .sort((a, b) => a.sortOrder - b.sortOrder)
    .map(category => ({ value: category.id, label: category.name, hint: category.description }));
  const coaOptions = eligibleAccounts.map(account => ({
    value: account.code,
    label: `${account.code} · ${account.name}`,
    hint: account.type.charAt(0).toUpperCase() + account.type.slice(1),
  }));

  const update = (field, value) => {
    setForm(current => ({ ...current, [field]: value, ...(field === 'type' ? { coaCode: '' } : {}) }));
    setErrors(current => ({ ...current, [field]: '' }));
  };

  const submit = event => {
    event.preventDefault();
    const nextErrors = {};
    const parsedAmount = Number(form.amount.replace(/\D/g, ''));
    if (!form.categoryId) nextErrors.categoryId = 'Pilih kategori.';
    if (!form.name.trim()) nextErrors.name = 'Nama Budget Item wajib diisi.';
    if (!form.type) nextErrors.type = 'Pilih tipe Budget Item.';
    if (!form.coaCode) nextErrors.coaCode = 'Pilih satu COA.';
    if (!form.amount || !Number.isFinite(parsedAmount)) nextErrors.amount = 'Nominal anggaran wajib diisi.';
    if (Object.keys(nextErrors).length) { setErrors(nextErrors); return; }

    onSave({
      ...item,
      categoryId: form.categoryId,
      name: form.name.trim(),
      description: form.description.trim(),
      type: form.type,
      coaCode: form.coaCode,
      active: form.active,
      amount: parsedAmount,
    });
  };

  const selectedAccount = ACC[form.coaCode];
  return (
    <div className="dialog-backdrop" onMouseDown={onClose}>
      <form className="dialog rapbs-dialog rapbs-item-dialog" onSubmit={submit} onMouseDown={event => event.stopPropagation()}>
        <div className="dialog-header rapbs-dialog-header">
          <div>
            <h2 className="dialog-title">{editing ? 'Ubah Budget Item' : 'Tambah Budget Item'}</h2>
            <p className="dialog-desc">Rencana Tahun Ajaran {academicYearName}. Satu item hanya dapat dipetakan ke satu COA.</p>
          </div>
          <button type="button" className="icon-button" aria-label="Tutup" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="dialog-body rapbs-item-form">
          <div className="field">
            <label>Kategori</label>
            <RapbsSelect value={form.categoryId} options={categoryOptions} placeholder="Pilih kategori..." onChange={value => update('categoryId', value)} />
            {errors.categoryId && <span className="field-error">{errors.categoryId}</span>}
          </div>

          <div className="field">
            <label>Tipe Budget Item</label>
            {editing ? (
              <div className={`rapbs-type-locked ${form.type}`}>
                <span className="rapbs-type-icon"><Icon name={form.type === 'income' ? 'arrowRight' : 'expense'} size={16} /></span>
                <span><strong>{RAPBS_TYPE_META[form.type].label}</strong><small>Tipe dikunci setelah item dibuat</small></span>
              </div>
            ) : (
              <div className="rapbs-type-options">
                {Object.entries(RAPBS_TYPE_META).map(([type, meta]) => (
                  <button type="button" key={type} className={'rapbs-type-option ' + type + (form.type === type ? ' selected' : '')} onClick={() => update('type', type)}>
                    <span className="rapbs-type-icon"><Icon name={type === 'income' ? 'arrowRight' : 'expense'} size={16} /></span>
                    <span><strong>{meta.label}</strong><small>{meta.description}</small></span>
                    {form.type === type && <Icon name="checkCircle" size={16} />}
                  </button>
                ))}
              </div>
            )}
            {errors.type && <span className="field-error">{errors.type}</span>}
          </div>

          <div className="field">
            <label htmlFor="rapbs-item-name">Nama Budget Item</label>
            <input id="rapbs-item-name" className={'input' + (errors.name ? ' invalid' : '')} placeholder="Contoh: Buku Perpustakaan" value={form.name} onChange={event => update('name', event.target.value)} />
            {errors.name && <span className="field-error">{errors.name}</span>}
          </div>

          <div className="field">
            <label htmlFor="rapbs-item-description">Deskripsi <span className="muted">(opsional)</span></label>
            <input id="rapbs-item-description" className="input" placeholder="Keterangan penggunaan anggaran" value={form.description} onChange={event => update('description', event.target.value)} />
          </div>

          <div className="field">
            <label>Mapping COA</label>
            {editing ? (
              <div className="rapbs-coa-locked">
                <span className="code">{selectedAccount?.code}</span>
                <span><strong>{selectedAccount?.name}</strong><small>Mapping dikunci untuk menjaga histori dan relasi 1:1</small></span>
                <Icon name="checkCircle" size={16} />
              </div>
            ) : (
              <RapbsSelect value={form.coaCode} options={coaOptions} placeholder={form.type ? 'Pilih COA yang belum dipetakan...' : 'Pilih tipe terlebih dahulu'} disabled={!form.type} onChange={value => update('coaCode', value)} />
            )}
            {errors.coaCode && <span className="field-error">{errors.coaCode}</span>}
            {!editing && form.type && <span className="field-help">{form.type === 'income' ? 'COA Pendapatan dan Liabilitas' : 'COA Beban, Aset, dan Liabilitas'} yang belum dipakai.</span>}
          </div>

          <div className="field">
            <label htmlFor="rapbs-item-amount">{form.type ? RAPBS_TYPE_META[form.type].itemLabel : 'Nominal Anggaran'} · {academicYearName}</label>
            <div className={'currency-input' + (errors.amount ? ' invalid' : '')}>
              <span>Rp</span>
              <input id="rapbs-item-amount" inputMode="numeric" placeholder="0" value={form.amount ? Number(form.amount).toLocaleString('id-ID') : ''} onChange={event => update('amount', event.target.value.replace(/\D/g, ''))} />
            </div>
            {errors.amount && <span className="field-error">{errors.amount}</span>}
          </div>

          {editing && (
            <label className="rapbs-status-toggle">
              <input type="checkbox" checked={form.active} onChange={event => update('active', event.target.checked)} />
              <span><strong>Item aktif</strong><small>Item nonaktif tidak dihitung dalam total rencana.</small></span>
            </label>
          )}
        </div>
        <div className="dialog-footer rapbs-dialog-footer">
          <Btn type="button" onClick={onClose}>Batal</Btn>
          <Btn type="submit" tone="primary">{editing ? 'Simpan Perubahan' : 'Simpan Item'}</Btn>
        </div>
      </form>
    </div>
  );
}

function RapbsPlanSection({ type, categories, items, budgets, year, academicYearName, canManage, onEdit, onRemove }) {
  const meta = RAPBS_TYPE_META[type];
  const typeItems = items.filter(item => item.type === type);
  const amountFor = itemId => budgets.find(budget => budget.year === year && budget.itemId === itemId)?.amount || 0;
  const total = typeItems.filter(item => item.active).reduce((sum, item) => sum + amountFor(item.id), 0);
  const grouped = [...categories]
    .sort((a, b) => a.sortOrder - b.sortOrder)
    .map(category => ({ category, items: typeItems.filter(item => item.categoryId === category.id) }))
    .filter(group => group.items.length);

  return (
    <section className={'rapbs-plan-section ' + type}>
      <div className="rapbs-plan-section-head">
        <div className="rapbs-plan-section-title">
          <span className="rapbs-plan-section-icon"><Icon name={type === 'income' ? 'arrowRight' : 'expense'} size={17} /></span>
          <div><h2>{meta.label}</h2><p>{meta.description}</p></div>
        </div>
        <div className="rapbs-plan-section-total"><span>Total Rencana</span><strong>{rupiah(total, { sym: true })}</strong></div>
      </div>

      {grouped.map(({ category, items: categoryItems }) => {
        const categoryTotal = categoryItems.filter(item => item.active).reduce((sum, item) => sum + amountFor(item.id), 0);
        return (
          <Card className="rapbs-category-card" key={category.id}>
            <div className="rapbs-category-card-head">
              <div><strong>{category.name}</strong><span>{category.description}</span></div>
              <div><span>Subtotal</span><strong>{rupiah(categoryTotal, { sym: true })}</strong></div>
            </div>
            <div className="table-wrap">
              <table className="table rapbs-plan-table">
                <thead><tr><th>Budget Item</th><th>Mapping COA</th><th>Status</th><th className="num">Anggaran {academicYearName}</th>{canManage && <th aria-label="Tindakan"></th>}</tr></thead>
                <tbody>
                  {categoryItems.map(item => {
                    const account = ACC[item.coaCode];
                    return (
                      <tr key={item.id} className={item.active ? '' : 'rapbs-item-inactive'}>
                        <td><strong>{item.name}</strong><small>{item.description}</small></td>
                        <td><span className="code">{item.coaCode}</span><small>{account?.name}</small></td>
                        <td><Pill tone={item.active ? 'ok' : 'neutral'}>{item.active ? 'Aktif' : 'Nonaktif'}</Pill></td>
                        <td className="num tnum"><strong>{rupiah(amountFor(item.id), { sym: true })}</strong></td>
                        {canManage && <td className="actions"><div className="rapbs-row-actions"><Btn tone="ghost" size="sm" icon="edit" onClick={() => onEdit(item)}>Ubah</Btn><button className="rapbs-remove-item" title={`Hapus ${item.name} dari ${academicYearName}`} onClick={() => onRemove(item)}><Icon name="trash" size={14} /></button></div></td>}
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </Card>
        );
      })}
    </section>
  );
}

function Rapbs({ role, pushToast }) {
  const {
    academicYears, setAcademicYears,
    yearScopes, setYearScopes,
    categories, setCategories,
    items, setItems,
    budgets, setBudgets,
    selectedAcademicYearId, setSelectedAcademicYearId,
    filter, setFilter,
  } = useBudgetStore();
  const [itemDialog, setItemDialog] = useState$rapbs(null);
  const [categoryDialogOpen, setCategoryDialogOpen] = useState$rapbs(false);
  const [academicYearDialogOpen, setAcademicYearDialogOpen] = useState$rapbs(false);
  const [reuseDialogOpen, setReuseDialogOpen] = useState$rapbs(false);
  const [removalTarget, setRemovalTarget] = useState$rapbs(null);
  const [rejectDialogOpen, setRejectDialogOpen] = useState$rapbs(false);
  const [historyDialogOpen, setHistoryDialogOpen] = useState$rapbs(false);
  const [importDialogOpen, setImportDialogOpen] = useState$rapbs(false);
  const canManage = ROLES[role].can.entry;
  const canApprove = ROLES[role].can.approve;
  const canExport = ROLES[role].can.export;
  const selectedAcademicYear = academicYears.find(academicYear => academicYear.id === selectedAcademicYearId) || null;
  const selectedScope = yearScopes.find(scope => scope.academicYearId === selectedAcademicYearId) || { categoryIds: [], itemIds: [], itemOverrides: {} };
  const year = selectedAcademicYear?.budgetYear || null;
  const scopedCategories = categories.filter(category => selectedScope.categoryIds.includes(category.id));
  const scopedItems = items.filter(item => selectedScope.itemIds.includes(item.id)).map(item => ({ ...item, ...(selectedScope.itemOverrides[item.id] || {}) }));
  const reusableItems = items.filter(item => !selectedScope.itemIds.includes(item.id)).map(item => ({ ...item, ...(selectedScope.itemOverrides[item.id] || {}) }));
  const canEdit = canManage && ['draft', 'rejected'].includes(selectedAcademicYear?.status);
  const approvalMeta = selectedAcademicYear ? RAPBS_APPROVAL_META[selectedAcademicYear.status] : null;
  const rejectionHistory = selectedAcademicYear?.approvalHistory.filter(event => event.action === 'rejected') || [];

  useEffect$rapbs(() => {
    setItemDialog(null);
    setCategoryDialogOpen(false);
    setAcademicYearDialogOpen(false);
    setReuseDialogOpen(false);
    setRemovalTarget(null);
    setRejectDialogOpen(false);
    setHistoryDialogOpen(false);
    setImportDialogOpen(false);
  }, [role, selectedAcademicYearId, selectedAcademicYear?.status]);

  const totals = useMemo$rapbs(() => {
    const amountFor = itemId => budgets.find(budget => budget.year === year && budget.itemId === itemId)?.amount || 0;
    return scopedItems.filter(item => item.active).reduce((result, item) => {
      result[item.type] += amountFor(item.id);
      result.count += 1;
      return result;
    }, { income: 0, outcome: 0, count: 0 });
  }, [scopedItems, budgets, year]);

  const updateSelectedScope = updater => {
    setYearScopes(current => current.map(scope => scope.academicYearId === selectedAcademicYearId ? updater(scope) : scope));
  };

  const addCategory = category => {
    if (!canEdit) return;
    const id = `cat-custom-${Date.now()}`;
    setCategories(current => [...current, { id, name: category.name, description: category.description, sortOrder: current.length + 1 }]);
    updateSelectedScope(scope => ({ ...scope, categoryIds: [...scope.categoryIds, id] }));
    pushToast({ type: 'success', msg: `Kategori ${category.name} ditambahkan ke TA ${selectedAcademicYear.name}.` });
  };

  const includeCategory = category => {
    if (!canEdit) return;
    updateSelectedScope(scope => ({ ...scope, categoryIds: [...new Set([...scope.categoryIds, category.id])] }));
    pushToast({ type: 'success', msg: `${category.name} digunakan pada TA ${selectedAcademicYear.name}.` });
  };

  const createAcademicYear = academicYear => {
    if (!canManage) return;
    const template = [...academicYears].sort((a, b) => b.endYear - a.endYear)[0];
    const templateScope = yearScopes.find(scope => scope.academicYearId === template?.id) || { categoryIds: [], itemIds: [] };
    setAcademicYears(current => [...current, academicYear].sort((a, b) => a.startYear - b.startYear));
    setYearScopes(current => [...current, {
      academicYearId: academicYear.id,
      categoryIds: [...templateScope.categoryIds],
      itemIds: [...templateScope.itemIds],
      itemOverrides: Object.fromEntries(Object.entries(templateScope.itemOverrides || {}).map(([id, override]) => [id, { ...override }])),
    }]);
    setSelectedAcademicYearId(academicYear.id);
    setAcademicYearDialogOpen(false);
    setFilter('all');
    pushToast({ type: 'success', msg: `Tahun Ajaran ${academicYear.name} dibuat sebagai Draft dengan nominal kosong.` });
  };

  const saveItem = form => {
    if (!canEdit) return;
    if (form.id) {
      updateSelectedScope(scope => ({
        ...scope,
        categoryIds: [...new Set([...scope.categoryIds, form.categoryId])],
        itemOverrides: { ...scope.itemOverrides, [form.id]: { categoryId: form.categoryId, name: form.name, description: form.description, active: form.active } },
      }));
      setBudgets(current => {
        const exists = current.some(budget => budget.year === year && budget.itemId === form.id);
        return exists ? current.map(budget => budget.year === year && budget.itemId === form.id ? { ...budget, amount: form.amount } : budget) : [...current, { year, itemId: form.id, amount: form.amount }];
      });
      pushToast({ type: 'success', msg: `${form.name} diperbarui untuk TA ${selectedAcademicYear.name}.` });
    } else {
      const id = `rapbs-custom-${Date.now()}`;
      setItems(current => [...current, { id, categoryId: form.categoryId, name: form.name, description: form.description, type: form.type, coaCode: form.coaCode, active: true }]);
      setBudgets(current => [...current, { year, itemId: id, amount: form.amount }]);
      updateSelectedScope(scope => ({ ...scope, categoryIds: [...new Set([...scope.categoryIds, form.categoryId])], itemIds: [...scope.itemIds, id] }));
      pushToast({ type: 'success', msg: `${form.name} ditambahkan ke RAPBS ${selectedAcademicYear.name}.` });
    }
    setItemDialog(null);
  };

  const handleTemplateDownload = () => {
    try {
      downloadRapbsTemplate({ academicYear: selectedAcademicYear, categories, items: scopedItems, budgets });
      pushToast({ type: 'success', msg: `Template RAPBS ${selectedAcademicYear.name} berhasil diunduh.` });
    } catch (error) {
      pushToast({ type: 'error', msg: error.message || 'Template RAPBS gagal diunduh.' });
    }
  };

  const importBudgetAmounts = changes => {
    if (!canEdit) return;
    const scopedItemIds = new Set(scopedItems.map(item => item.id));
    const validChanges = changes.filter(change => scopedItemIds.has(change.itemId) && Number.isSafeInteger(change.amount) && change.amount >= 0);
    if (!validChanges.length) return;
    const amountByItemId = new Map(validChanges.map(change => [change.itemId, change.amount]));
    setBudgets(current => {
      const updatedItemIds = new Set();
      const next = current.map(budget => {
        if (budget.year !== year || !amountByItemId.has(budget.itemId)) return budget;
        updatedItemIds.add(budget.itemId);
        return { ...budget, amount: amountByItemId.get(budget.itemId) };
      });
      amountByItemId.forEach((amount, itemId) => {
        if (!updatedItemIds.has(itemId)) next.push({ year, itemId, amount });
      });
      return next;
    });
    setImportDialogOpen(false);
    pushToast({ type: 'success', msg: `${validChanges.length} nominal RAPBS ${selectedAcademicYear.name} berhasil diimpor.` });
  };

  const reuseItem = (item, amount) => {
    if (!canEdit) return;
    updateSelectedScope(scope => ({ ...scope, categoryIds: [...new Set([...scope.categoryIds, item.categoryId])], itemIds: [...scope.itemIds, item.id] }));
    setBudgets(current => [...current.filter(budget => !(budget.year === year && budget.itemId === item.id)), { year, itemId: item.id, amount }]);
    setReuseDialogOpen(false);
    pushToast({ type: 'success', msg: `${item.name} digunakan kembali pada TA ${selectedAcademicYear.name}.` });
  };

  const confirmRemoval = () => {
    if (!canEdit || !removalTarget) return;
    if (removalTarget.kind === 'item') {
      const item = removalTarget.value;
      updateSelectedScope(scope => ({ ...scope, itemIds: scope.itemIds.filter(id => id !== item.id) }));
      setBudgets(current => current.filter(budget => !(budget.year === year && budget.itemId === item.id)));
      pushToast({ type: 'success', msg: `${item.name} dihapus dari TA ${selectedAcademicYear.name}.` });
    } else {
      const category = removalTarget.value;
      const removedItemIds = scopedItems.filter(item => item.categoryId === category.id).map(item => item.id);
      updateSelectedScope(scope => ({ ...scope, categoryIds: scope.categoryIds.filter(id => id !== category.id), itemIds: scope.itemIds.filter(id => !removedItemIds.includes(id)) }));
      setBudgets(current => current.filter(budget => !(budget.year === year && removedItemIds.includes(budget.itemId))));
      pushToast({ type: 'success', msg: `${category.name} dan itemnya dihapus dari TA ${selectedAcademicYear.name}.` });
    }
    setRemovalTarget(null);
  };

  const addApprovalEvent = (status, action, note) => {
    const actor = ROLES[role];
    setAcademicYears(current => current.map(academicYear => academicYear.id === selectedAcademicYearId ? {
      ...academicYear,
      status,
      approvalHistory: [...academicYear.approvalHistory, { id: `approval-${Date.now()}`, action, actor: actor.name, role: actor.title, at: new Date().toISOString(), note }],
    } : academicYear));
  };

  const submitForApproval = () => {
    if (!canEdit) return;
    if (!scopedItems.length || totals.income + totals.outcome <= 0) {
      pushToast({ type: 'error', msg: 'RAPBS harus memiliki Budget Item dan nominal sebelum diajukan.' });
      return;
    }
    addApprovalEvent('pending', 'submitted', selectedAcademicYear.status === 'rejected' ? 'RAPBS diajukan ulang setelah revisi.' : 'RAPBS diajukan untuk persetujuan.');
    pushToast({ type: 'success', msg: `RAPBS ${selectedAcademicYear.name} menunggu persetujuan Kepala Sekolah.` });
  };

  const approveRapbs = () => {
    if (!canApprove || selectedAcademicYear.status !== 'pending') return;
    addApprovalEvent('approved', 'approved', 'RAPBS telah ditinjau dan disetujui.');
    pushToast({ type: 'success', msg: `RAPBS ${selectedAcademicYear.name} disetujui.` });
  };

  const rejectRapbs = reason => {
    if (!canApprove || selectedAcademicYear.status !== 'pending') return;
    addApprovalEvent('rejected', 'rejected', reason);
    setRejectDialogOpen(false);
    pushToast({ type: 'error', msg: `RAPBS ${selectedAcademicYear.name} ditolak dan dikembalikan untuk revisi.` });
  };

  const editAmount = itemDialog?.id ? budgets.find(budget => budget.year === year && budget.itemId === itemDialog.id)?.amount || 0 : 0;
  const balance = totals.income - totals.outcome;
  const suggestedStartYear = Math.max(...academicYears.map(academicYear => academicYear.endYear), new Date().getFullYear());

  if (!selectedAcademicYear) {
    return (
      <div className="rapbs-year-gate">
        <div className="rapbs-year-gate-head"><span className="rapbs-year-gate-icon"><Icon name="planning" size={22} /></span><div><span className="rapbs-eyebrow">RAPBS</span><h1>Pilih Tahun Ajaran</h1><p>Pilih periode perencanaan yang akan dibuka atau buat Tahun Ajaran baru terlebih dahulu.</p></div></div>
        <div className="rapbs-academic-year-grid">
          {academicYears.map(academicYear => {
            const scope = yearScopes.find(value => value.academicYearId === academicYear.id) || { itemIds: [], itemOverrides: {} };
            const activeItemIds = new Set(items.filter(item => scope.itemIds.includes(item.id)).filter(item => (scope.itemOverrides[item.id]?.active ?? item.active)).map(item => item.id));
            const yearBudgets = budgets.filter(budget => budget.year === academicYear.budgetYear && activeItemIds.has(budget.itemId));
            const plannedItems = yearBudgets.filter(budget => budget.amount > 0).length;
            const totalPlan = yearBudgets.reduce((sum, budget) => sum + budget.amount, 0);
            const statusMeta = RAPBS_APPROVAL_META[academicYear.status];
            return <button className="rapbs-academic-year-card" key={academicYear.id} onClick={() => { setSelectedAcademicYearId(academicYear.id); setFilter('all'); }}><span className="rapbs-academic-year-card-icon"><Icon name="planning" size={18} /></span><span className="rapbs-academic-year-card-copy"><small>Tahun Ajaran</small><strong>{academicYear.name}</strong><span>{plannedItems ? `${plannedItems} item · ${rupiah(totalPlan, { compact: true })}` : 'Belum ada nominal anggaran'}</span></span><Pill tone={statusMeta.tone}>{statusMeta.label}</Pill><span className="rapbs-academic-year-open">Buka <Icon name="chevronRight" size={15} /></span></button>;
          })}
        </div>
        <div className="rapbs-year-gate-create"><span>Belum menemukan Tahun Ajaran yang sesuai?</span>{canManage ? <Btn tone="primary" icon="plus" onClick={() => setAcademicYearDialogOpen(true)}>Buat Tahun Ajaran</Btn> : <small>Hubungi Bendahara untuk membuat Tahun Ajaran baru.</small>}</div>
        {academicYearDialogOpen && <AcademicYearDialog academicYears={academicYears} suggestedStartYear={suggestedStartYear} onClose={() => setAcademicYearDialogOpen(false)} onCreate={createAcademicYear} />}
      </div>
    );
  }

  const latestRejection = rejectionHistory.at(-1);
  return (
    <div className="rapbs-page col gap-4">
      <div className="rapbs-heading">
        <div><div className="rapbs-title-row"><h1>RAPBS</h1><Pill tone={approvalMeta.tone}>{approvalMeta.label}</Pill></div><p className="lede">Susun target pendapatan dan anggaran belanja per Budget Item untuk satu tahun.</p></div>
        <div className="rapbs-heading-actions">
          <label className="rapbs-year-picker"><span>Tahun Ajaran</span><select value={selectedAcademicYearId} onChange={event => { setSelectedAcademicYearId(event.target.value); setFilter('all'); }}><option value={selectedAcademicYearId}>{selectedAcademicYear.name}</option></select></label>
          <Btn icon="chevronLeft" onClick={() => setSelectedAcademicYearId(null)}>Ganti Tahun Ajaran</Btn>
          {canExport && <Btn icon="download" onClick={handleTemplateDownload}>Unduh Template</Btn>}
          {canEdit && <Btn icon="upload" onClick={() => setImportDialogOpen(true)}>Impor Anggaran</Btn>}
          <Btn icon="listChecks" onClick={() => setCategoryDialogOpen(true)}>Kategori</Btn>
          {canEdit && reusableItems.length > 0 && <Btn icon="arrowRight" onClick={() => setReuseDialogOpen(true)}>Gunakan Item Lama</Btn>}
          {canEdit && <Btn tone="primary" icon="plus" onClick={() => setItemDialog({})}>Tambah Item</Btn>}
        </div>
      </div>

      <div className={'rapbs-approval-bar ' + selectedAcademicYear.status}>
        <div><span className="rapbs-approval-icon"><Icon name={selectedAcademicYear.status === 'approved' ? 'checkCircle' : selectedAcademicYear.status === 'rejected' ? 'alert' : 'clock'} size={17} /></span><span><strong>{approvalMeta.label}</strong><small>{selectedAcademicYear.status === 'draft' ? 'Lengkapi rencana lalu ajukan kepada Kepala Sekolah.' : selectedAcademicYear.status === 'pending' ? 'Rencana terkunci selama menunggu keputusan Kepala Sekolah.' : selectedAcademicYear.status === 'approved' ? 'Rencana telah disetujui dan terkunci.' : 'Perbaiki catatan penolakan lalu ajukan ulang.'}</small></span></div>
        <div className="rapbs-approval-actions">
          {selectedAcademicYear.approvalHistory.length > 0 && <Btn icon="clock" onClick={() => setHistoryDialogOpen(true)}>Riwayat ({selectedAcademicYear.approvalHistory.length})</Btn>}
          {canEdit && <Btn tone="primary" icon="arrowRight" onClick={submitForApproval}>{selectedAcademicYear.status === 'rejected' ? 'Ajukan Ulang' : 'Ajukan Persetujuan'}</Btn>}
          {canApprove && selectedAcademicYear.status === 'pending' && <><Btn tone="destructive" icon="x" onClick={() => setRejectDialogOpen(true)}>Tolak</Btn><Btn tone="primary" icon="check" onClick={approveRapbs}>Setujui</Btn></>}
        </div>
      </div>

      {latestRejection && <div className="rapbs-rejection-banner"><span className="rapbs-rejection-icon"><Icon name="x" size={16} /></span><div><strong>Catatan Penolakan Terakhir</strong><p>{latestRejection.note}</p><small>{latestRejection.actor} · {new Date(latestRejection.at).toLocaleString('id-ID', { dateStyle: 'medium', timeStyle: 'short' })}</small></div><button onClick={() => setHistoryDialogOpen(true)}>Lihat riwayat</button></div>}

      <div className="rapbs-notice"><Icon name="alert" size={17} /><div><strong>Tahun Ajaran {selectedAcademicYear.name}</strong><span>Tahun anggaran {year}. Category dan Item hanya dihapus dari Tahun Ajaran aktif, bukan dari data master.</span></div></div>

      <div className="rapbs-summary-grid">
        <div className="rapbs-summary income"><span>Target Pendapatan</span><strong>{rupiah(totals.income, { sym: true })}</strong><small>{scopedItems.filter(item => item.type === 'income' && item.active).length} Budget Item aktif</small></div>
        <div className="rapbs-summary outcome"><span>Anggaran Belanja</span><strong>{rupiah(totals.outcome, { sym: true })}</strong><small>{scopedItems.filter(item => item.type === 'outcome' && item.active).length} Budget Item aktif</small></div>
        <div className={'rapbs-summary balance ' + (balance >= 0 ? 'positive' : 'negative')}><span>Surplus / Defisit Rencana</span><strong>{rupiah(balance, { sym: true })}</strong><small>{balance >= 0 ? 'Rencana pendapatan mencukupi' : 'Anggaran perlu diseimbangkan'}</small></div>
        <div className="rapbs-summary"><span>Total Budget Item</span><strong>{totals.count}</strong><small>Aktif pada TA {selectedAcademicYear.name}</small></div>
      </div>

      <div className="rapbs-filter-bar"><div className="tabs">{[['all', 'Semua'], ['income', 'Pendapatan'], ['outcome', 'Belanja']].map(([value, label]) => <button key={value} className={'tab' + (filter === value ? ' active' : '')} onClick={() => setFilter(value)}>{label}</button>)}</div><span>{canEdit ? 'Draft dapat diubah sebelum diajukan.' : 'Rencana terkunci pada status saat ini.'}</span></div>

      {(filter === 'all' || filter === 'income') && <RapbsPlanSection type="income" categories={scopedCategories} items={scopedItems} budgets={budgets} year={year} academicYearName={selectedAcademicYear.name} canManage={canEdit} onEdit={setItemDialog} onRemove={item => setRemovalTarget({ kind: 'item', value: item })} />}
      {(filter === 'all' || filter === 'outcome') && <RapbsPlanSection type="outcome" categories={scopedCategories} items={scopedItems} budgets={budgets} year={year} academicYearName={selectedAcademicYear.name} canManage={canEdit} onEdit={setItemDialog} onRemove={item => setRemovalTarget({ kind: 'item', value: item })} />}

      {categoryDialogOpen && <CategoryDialog categories={categories} includedCategoryIds={selectedScope.categoryIds} academicYearName={selectedAcademicYear.name} canManage={canEdit} onClose={() => setCategoryDialogOpen(false)} onAdd={addCategory} onInclude={includeCategory} onRemove={category => { setCategoryDialogOpen(false); setRemovalTarget({ kind: 'category', value: category }); }} />}
      {itemDialog && <BudgetItemDialog item={itemDialog.id ? itemDialog : null} year={year} academicYearName={selectedAcademicYear.name} amount={editAmount} categories={scopedCategories} items={items} onClose={() => setItemDialog(null)} onSave={saveItem} />}
      {reuseDialogOpen && <ReuseItemDialog items={reusableItems} academicYearName={selectedAcademicYear.name} onClose={() => setReuseDialogOpen(false)} onReuse={reuseItem} />}
      {removalTarget && <RemoveFromYearDialog target={removalTarget} academicYearName={selectedAcademicYear.name} onClose={() => setRemovalTarget(null)} onConfirm={confirmRemoval} />}
      {rejectDialogOpen && <RejectRapbsDialog academicYearName={selectedAcademicYear.name} onClose={() => setRejectDialogOpen(false)} onReject={rejectRapbs} />}
      {historyDialogOpen && <ApprovalHistoryDialog academicYear={selectedAcademicYear} onClose={() => setHistoryDialogOpen(false)} />}
      {importDialogOpen && <RapbsImportDialog academicYear={selectedAcademicYear} categories={categories} items={scopedItems} budgets={budgets} onClose={() => setImportDialogOpen(false)} onDownload={handleTemplateDownload} onImport={importBudgetAmounts} />}
      {academicYearDialogOpen && <AcademicYearDialog academicYears={academicYears} suggestedStartYear={suggestedStartYear} onClose={() => setAcademicYearDialogOpen(false)} onCreate={createAcademicYear} />}
    </div>
  );
}

Object.assign(window, { RapbsSelect, CategoryDialog, AcademicYearDialog, ReuseItemDialog, RemoveFromYearDialog, RejectRapbsDialog, ApprovalHistoryDialog, RapbsImportDialog, BudgetItemDialog, RapbsPlanSection, Rapbs });
