import React, { useMemo, useState } from 'react';
import { createRoot } from 'react-dom/client';
import {
  CalendarDays,
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  CircleDollarSign,
  Landmark,
  Plus,
  ReceiptText,
  Target,
  Trash2,
  TrendingDown,
  WalletCards,
  Sparkles,
} from 'lucide-react';
import './styles.css';

const STORAGE_KEY = 'uang-makan-dashboard-v1';
const DEFAULT_BUDGET = 1600000;
const CATEGORIES = ['Makan', 'Minum', 'Transport', 'Jajan', 'Lainnya'];

const today = new Date();
const yyyyMmDd = (date) => date.toISOString().slice(0, 10);
const currentMonth = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`;
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'];

const defaultState = {
  income: DEFAULT_BUDGET,
  payday: 1,
  month: currentMonth,
  incomeDate: yyyyMmDd(today),
  expenses: [],
};

function loadState() {
  try {
    const saved = JSON.parse(localStorage.getItem(STORAGE_KEY));
    if (!saved) return defaultState;
    const merged = { ...defaultState, ...saved };
    if (!merged.incomeDate) {
      merged.incomeDate = `${merged.month}-${String(merged.payday).padStart(2, '0')}`;
    }
    return merged;
  } catch {
    return defaultState;
  }
}

function saveState(next) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
}

function formatRupiah(value) {
  return new Intl.NumberFormat('id-ID', {
    style: 'currency',
    currency: 'IDR',
    maximumFractionDigits: 0,
  }).format(Number.isFinite(value) ? value : 0);
}

function formatDate(value) {
  return new Intl.DateTimeFormat('id-ID', {
    day: '2-digit',
    month: 'short',
  }).format(new Date(`${value}T00:00:00`));
}

function formatLongDate(value) {
  return new Intl.DateTimeFormat('id-ID', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
  }).format(new Date(`${value}T00:00:00`));
}

function formatMonth(value) {
  const [year, month] = value.split('-').map(Number);
  return `${monthNames[month - 1]} ${year}`;
}

function clampDay(year, monthIndex, day) {
  return Math.min(day, new Date(year, monthIndex + 1, 0).getDate());
}

function getCycleRange(monthValue, payday) {
  const [year, month] = monthValue.split('-').map(Number);
  const monthIndex = month - 1;
  const start = new Date(year, monthIndex, clampDay(year, monthIndex, payday));
  const next = new Date(year, monthIndex + 1, clampDay(year, monthIndex + 1, payday));
  return { start, next };
}

function daysBetween(start, end) {
  const oneDay = 24 * 60 * 60 * 1000;
  return Math.max(0, Math.ceil((end - start) / oneDay));
}

function App() {
  const [data, setData] = useState(loadState);
  const [draft, setDraft] = useState({
    date: yyyyMmDd(today),
    category: 'Makan',
    note: '',
    amount: '',
  });
  const [categoryOpen, setCategoryOpen] = useState(false);
  const [monthOpen, setMonthOpen] = useState(false);

  const updateData = (patch) => {
    setData((current) => {
      const next = typeof patch === 'function' ? patch(current) : { ...current, ...patch };
      saveState(next);
      return next;
    });
  };

  const updateIncomeDate = (dateValue) => {
    const date = new Date(`${dateValue}T00:00:00`);
    updateData({
      incomeDate: dateValue,
      month: `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`,
      payday: date.getDate(),
    });
  };

  const stats = useMemo(() => {
    const { start, next } = getCycleRange(data.month, Number(data.payday));
    const todayValue = yyyyMmDd(today);
    const totalExpense = data.expenses.reduce((sum, item) => sum + Number(item.amount || 0), 0);
    const todayExpense = data.expenses
      .filter((item) => item.date === todayValue)
      .reduce((sum, item) => sum + Number(item.amount || 0), 0);
    const expenseBeforeToday = data.expenses
      .filter((item) => item.date < todayValue)
      .reduce((sum, item) => sum + Number(item.amount || 0), 0);
    const remaining = Number(data.income || 0) - totalExpense;
    const todayAtStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());
    const activeDate = todayAtStart < start ? start : todayAtStart;
    const daysLeft = Math.max(1, daysBetween(activeDate, next));
    const cycleDays = Math.max(1, daysBetween(start, next));
    const elapsedDays = Math.min(cycleDays, Math.max(0, daysBetween(start, activeDate)));
    const balanceBeforeToday = Number(data.income || 0) - expenseBeforeToday;
    const dailyLimit = balanceBeforeToday / daysLeft - todayExpense;
    const nextDailyLimit = remaining / daysLeft;
    const idealSpent = (Number(data.income || 0) / cycleDays) * elapsedDays;
    const pace = idealSpent - totalExpense;
    const usedPercent = Math.min(100, Math.max(0, (totalExpense / Number(data.income || 1)) * 100));

    return { totalExpense, remaining, daysLeft, dailyLimit, nextDailyLimit, pace, usedPercent };
  }, [data]);

  const sortedExpenses = [...data.expenses].sort((a, b) => b.date.localeCompare(a.date));

  const addExpense = (event) => {
    event.preventDefault();
    const amount = Number(draft.amount);
    if (!amount || amount <= 0) return;

    updateData((current) => ({
      ...current,
      expenses: [
        ...current.expenses,
        {
          id: crypto.randomUUID(),
          date: draft.date,
          category: draft.category.trim() || 'Lainnya',
          note: draft.note.trim(),
          amount,
        },
      ],
    }));

    setDraft((current) => ({ ...current, note: '', amount: '' }));
  };

  const deleteExpense = (id) => {
    updateData((current) => ({
      ...current,
      expenses: current.expenses.filter((item) => item.id !== id),
    }));
  };

  return (
    <main className="app-shell">
      <header className="topbar">
        <div>
          <span>Monthly allowance OS</span>
          <h1>Uang Makan</h1>
        </div>
        <div className="topbar-date">
          <CalendarDays size={18} />
          {new Intl.DateTimeFormat('id-ID', { day: '2-digit', month: 'long', year: 'numeric' }).format(today)}
        </div>
      </header>

      <section className="dashboard-grid">
        <article className="balance-card">
          <div className="card-label">
            <WalletCards size={18} />
            <span>Saldo tersisa</span>
          </div>
          <strong>{formatRupiah(stats.remaining)}</strong>
          <div className="progress-track">
            <span style={{ width: `${stats.usedPercent}%` }} />
          </div>
          <div className="balance-meta">
            <span>{stats.usedPercent.toFixed(0)}% terpakai</span>
            <span>{formatRupiah(stats.totalExpense)} keluar</span>
          </div>
        </article>

        <article className={stats.dailyLimit < 0 ? 'daily-card danger' : 'daily-card'}>
          <div className="card-label">
            <Target size={18} />
            <span>Batas aman hari ini</span>
          </div>
          <strong>{formatRupiah(stats.dailyLimit)}</strong>
          <p>{stats.daysLeft} hari sampai cair berikutnya</p>
        </article>

        <section className="settings-card" aria-label="Pengaturan uang makan">
          <label className="field">
            <span><Landmark size={15} /> Uang masuk</span>
            <input
              type="number"
              value={data.income}
              min="0"
              onChange={(event) => updateData({ income: Number(event.target.value) })}
            />
          </label>
          <label className="field">
            <span><CalendarDays size={15} /> Tanggal masuk</span>
            <DatePicker
              value={data.incomeDate}
              onChange={updateIncomeDate}
            />
          </label>
          <label className="field">
            <span><CalendarDays size={15} /> Bulan</span>
            <MonthPicker
              value={data.month}
              open={monthOpen}
              onOpenChange={setMonthOpen}
              onChange={(month) => updateData({
                month,
                incomeDate: `${month}-${String(data.payday).padStart(2, '0')}`,
              })}
            />
          </label>
          <label className="field">
            <span><CircleDollarSign size={15} /> Cair tanggal</span>
            <input
              type="number"
              value={data.payday}
              min="1"
              max="31"
              onChange={(event) => {
                const payday = Number(event.target.value);
                updateData({
                  payday,
                  incomeDate: `${data.month}-${String(payday).padStart(2, '0')}`,
                });
              }}
            />
          </label>
        </section>

        <section className="stats-grid" aria-label="Ringkasan pengeluaran">
          <Metric icon={TrendingDown} label="Total keluar" value={formatRupiah(stats.totalExpense)} />
          <Metric icon={CalendarDays} label="Sisa hari" value={`${stats.daysLeft} hari`} />
          <Metric
            icon={CircleDollarSign}
            label="Maks besok"
            value={formatRupiah(stats.nextDailyLimit)}
            accent={stats.nextDailyLimit >= 0 ? 'green' : 'red'}
          />
        </section>
      </section>

      <section className="workspace">
        <form className="expense-form" onSubmit={addExpense}>
          <div className="section-title">
            <span><Sparkles size={17} /> Tambah Pengeluaran</span>
            <strong>{formatRupiah(Number(draft.amount || 0))}</strong>
          </div>
          <label className="field">
            <span>Tanggal</span>
            <DatePicker
              value={draft.date}
              onChange={(date) => setDraft({ ...draft, date })}
            />
          </label>
          <label className="field">
            <span>Kategori</span>
            <div className="custom-select">
              <button
                className="select-trigger"
                type="button"
                aria-haspopup="listbox"
                aria-expanded={categoryOpen}
                onClick={() => setCategoryOpen((open) => !open)}
              >
                {draft.category}
                <ChevronDown size={16} />
              </button>
              {categoryOpen ? (
                <div className="select-menu" role="listbox">
                  {CATEGORIES.map((category) => (
                    <button
                      className={category === draft.category ? 'select-option selected' : 'select-option'}
                      type="button"
                      role="option"
                      aria-selected={category === draft.category}
                      key={category}
                      onClick={() => {
                        setDraft({ ...draft, category });
                        setCategoryOpen(false);
                      }}
                    >
                      {category}
                    </button>
                  ))}
                </div>
              ) : null}
            </div>
          </label>
          <label className="field">
            <span>Nominal</span>
            <input
              type="number"
              min="0"
              placeholder="25000"
              value={draft.amount}
              onChange={(event) => setDraft({ ...draft, amount: event.target.value })}
            />
          </label>
          <label className="field note-field">
            <span>Catatan</span>
            <input
              type="text"
              placeholder="Nasi padang, kopi, dll"
              value={draft.note}
              onChange={(event) => setDraft({ ...draft, note: event.target.value })}
            />
          </label>
          <button className="primary-button" type="submit">
            <Plus size={18} /> Tambah
          </button>
        </form>

        <section className="expense-list">
          <div className="section-title">
            <span><ReceiptText size={17} /> Riwayat Harian</span>
            <strong>{sortedExpenses.length} transaksi</strong>
          </div>
          {sortedExpenses.length === 0 ? (
            <div className="empty-state">
              <ReceiptText size={34} />
              <span>Belum ada pengeluaran</span>
            </div>
          ) : (
            <div className="rows">
              {sortedExpenses.map((item) => (
                <article className="expense-row" key={item.id}>
                  <div className="date-pill">{formatDate(item.date)}</div>
                  <div className="expense-main">
                    <strong>{item.category}</strong>
                    <span>{item.note || 'Tanpa catatan'}</span>
                  </div>
                  <div className="expense-amount">{formatRupiah(Number(item.amount))}</div>
                  <button
                    className="icon-button"
                    type="button"
                    aria-label={`Hapus pengeluaran ${item.category}`}
                    onClick={() => deleteExpense(item.id)}
                  >
                    <Trash2 size={16} />
                  </button>
                </article>
              ))}
            </div>
          )}
        </section>
      </section>
    </main>
  );
}

function Metric({ icon: Icon, label, value, accent = 'default' }) {
  return (
    <article className={`metric metric-${accent}`}>
      <div className="metric-icon"><Icon size={19} /></div>
      <span>{label}</span>
      <strong>{value}</strong>
    </article>
  );
}

function DatePicker({ value, onChange }) {
  const selected = new Date(`${value}T00:00:00`);
  const [open, setOpen] = useState(false);
  const [viewDate, setViewDate] = useState(new Date(selected.getFullYear(), selected.getMonth(), 1));
  const year = viewDate.getFullYear();
  const month = viewDate.getMonth();
  const firstDay = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const prevDays = new Date(year, month, 0).getDate();
  const cells = Array.from({ length: 42 }, (_, index) => {
    const dayNumber = index - firstDay + 1;
    if (dayNumber < 1) return { day: prevDays + dayNumber, muted: true, date: new Date(year, month - 1, prevDays + dayNumber) };
    if (dayNumber > daysInMonth) return { day: dayNumber - daysInMonth, muted: true, date: new Date(year, month + 1, dayNumber - daysInMonth) };
    return { day: dayNumber, muted: false, date: new Date(year, month, dayNumber) };
  });

  return (
    <div className="custom-select date-picker">
      <button className="select-trigger" type="button" onClick={() => setOpen((state) => !state)}>
        {formatLongDate(value)}
        <CalendarDays size={16} />
      </button>
      {open ? (
        <div className="picker-menu">
          <div className="picker-head">
            <button type="button" onClick={() => setViewDate(new Date(year, month - 1, 1))}><ChevronLeft size={16} /></button>
            <strong>{formatMonth(`${year}-${String(month + 1).padStart(2, '0')}`)}</strong>
            <button type="button" onClick={() => setViewDate(new Date(year, month + 1, 1))}><ChevronRight size={16} /></button>
          </div>
          <div className="calendar-grid weekdays">
            {['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'].map((day) => <span key={day}>{day}</span>)}
          </div>
          <div className="calendar-grid">
            {cells.map((cell) => {
              const cellValue = yyyyMmDd(cell.date);
              return (
                <button
                  className={`${cell.muted ? 'muted ' : ''}${cellValue === value ? 'active' : ''}`}
                  type="button"
                  key={cellValue}
                  onClick={() => {
                    onChange(cellValue);
                    setOpen(false);
                  }}
                >
                  {cell.day}
                </button>
              );
            })}
          </div>
        </div>
      ) : null}
    </div>
  );
}

function MonthPicker({ value, open, onOpenChange, onChange }) {
  const [year, setYear] = useState(Number(value.split('-')[0]));
  const selectedMonth = Number(value.split('-')[1]);

  return (
    <div className="custom-select month-picker">
      <button className="select-trigger" type="button" onClick={() => onOpenChange(!open)}>
        {formatMonth(value)}
        <CalendarDays size={16} />
      </button>
      {open ? (
        <div className="picker-menu">
          <div className="picker-head">
            <button type="button" onClick={() => setYear((current) => current - 1)}><ChevronLeft size={16} /></button>
            <strong>{year}</strong>
            <button type="button" onClick={() => setYear((current) => current + 1)}><ChevronRight size={16} /></button>
          </div>
          <div className="month-grid">
            {monthNames.map((month, index) => {
              const monthValue = `${year}-${String(index + 1).padStart(2, '0')}`;
              return (
                <button
                  className={year === Number(value.split('-')[0]) && index + 1 === selectedMonth ? 'active' : ''}
                  type="button"
                  key={month}
                  onClick={() => {
                    onChange(monthValue);
                    onOpenChange(false);
                  }}
                >
                  {month}
                </button>
              );
            })}
          </div>
        </div>
      ) : null}
    </div>
  );
}

createRoot(document.getElementById('root')).render(<App />);
