import { useEffect, useMemo, useState } from 'preact/hooks';
import { errText, formatCents, j } from '../api';
import { DayChart } from './DayChart';

interface Totals { bar_id: number; bar_name: string; tx_count: number; paid_cents: number; crew_count: number; pfand_returns: number }
interface PerDrink { drink_id: number; drink_name: string; sold_qty: number }
interface ByDay { day: string; tx_count: number; paid_cents: number; crew_count: number; pfand_returns: number }
interface PerDrinkByDay { day: string; drinks: PerDrink[] }
interface ByHour { hour: string; tx_count: number; paid_cents: number; crew_count: number; pfand_returns: number }
interface ByHourOfDay { hour_of_day: number; tx_count: number; paid_cents: number; crew_count: number; pfand_returns: number }
interface StatsData {
  totals: Totals[];
  per_drink: PerDrink[];
  by_day: ByDay[];
  per_drink_by_day: PerDrinkByDay[];
  by_hour: ByHour[];
  by_hour_of_day: ByHourOfDay[];
}

function fmtDay(day: string): string {
  const [y, m, d] = day.split('-');
  return `${d}.${m}.${y}`;
}

// Shorter, no-year form for chart axis labels — the table above already
// carries the full date, and a year suffix on every tick just crowds a
// narrow axis for what's realistically a single-event date range anyway.
function fmtDayShort(day: string): string {
  const [, m, d] = day.split('-');
  return `${d}.${m}.`;
}

// `hour` is a "YYYY-MM-DD HH:00" bucket (see localHourBucket() server-side)
// — render as "30.07. 14h" for the continuous timeline chart.
function fmtHour(hour: string): string {
  const [day, time] = hour.split(' ');
  const [, m, d] = (day ?? '').split('-');
  const h = (time ?? '').split(':')[0];
  return `${d}.${m}. ${h}h`;
}

// Auth here isn't a full <Admin/>-style flow — GET /api/stats itself is the
// source of truth for whether a login is needed (STATS_PUBLIC can make it
// open with no session at all), so we just try the fetch and fall back to
// the same admin login form on a 401, rather than pre-checking /admin/api/me
// like Admin.tsx does.
export function Stats() {
  const [data, setData] = useState<StatsData | null>(null);
  const [needsLogin, setNeedsLogin] = useState(false);
  const [err, setErr] = useState<string | null>(null);
  // Separate from "did /api/stats succeed" — STATS_PUBLIC can make that
  // true with no session at all, but "Statistik zurücksetzen" deletes
  // every transaction and must stay gated on an actual admin login
  // regardless of STATS_PUBLIC (the server enforces this too; this is just
  // to not show a button that always 401s for a public/anonymous viewer).
  const [isAdmin, setIsAdmin] = useState(false);

  function reload() {
    setErr(null);
    fetch('/admin/api/me').then(j).then(r => setIsAdmin(!!r.authed)).catch(() => setIsAdmin(false));
    fetch('/api/stats')
      .then(async res => {
        if (res.status === 401) { setNeedsLogin(true); return; }
        if (!res.ok) throw new Error(await errText(res));
        setNeedsLogin(false);
        setData(await res.json());
      })
      .catch(e => setErr(String(e)));
  }
  useEffect(reload, []);

  if (needsLogin) return <Login onAuthed={reload} />;
  if (err) return <div class="admin"><p style="color:var(--danger)">{err}</p></div>;
  if (!data) return <div class="admin"><p>Lade Statistik…</p></div>;
  return <Dashboard data={data} isAdmin={isAdmin} onReset={reload} />;
}

function Login({ onAuthed }: { onAuthed: () => void }) {
  const [pw, setPw] = useState('');
  const [err, setErr] = useState<string | null>(null);

  async function submit(e: Event) {
    e.preventDefault();
    setErr(null);
    try {
      await fetch('/admin/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ password: pw }),
      }).then(j);
      onAuthed();
    } catch (e: any) {
      setErr(String(e));
    }
  }

  return (
    <form class="admin" onSubmit={submit}>
      <div class="login">
        <h1>Statistik</h1>
        <input
          type="password"
          placeholder="Passwort"
          value={pw}
          onInput={(e: any) => setPw(e.currentTarget.value)}
        />
        {err && <div style="color:var(--danger)">{err}</div>}
        <button type="submit">Anmelden</button>
      </div>
    </form>
  );
}

function Dashboard({ data, isAdmin, onReset }: { data: StatsData; isAdmin: boolean; onReset: () => void }) {
  async function reset() {
    if (!confirm('Wirklich die gesamte Statistik (alle Transaktionen) unwiderruflich löschen?')) return;
    const res = await fetch('/admin/api/stats/reset', { method: 'POST' });
    if (!res.ok) { alert(`Fehler: ${await errText(res)}`); return; }
    onReset();
  }

  // by_day/per_drink_by_day come pre-sorted from the server (by_day newest
  // first for the table below, per_drink_by_day oldest first for the
  // chart's left-to-right reading order) — no re-sort needed here.
  //
  // Each of these produces the full {labels, series} shape DayChart wants,
  // memoized as one object (#55) — building `series`/`labels` as fresh
  // array/object literals inline in JSX on every render defeats DayChart's
  // effect (keyed on those props by reference, not deep-equality), so
  // Dashboard re-rendering for any unrelated reason (e.g. the isAdmin
  // check resolving after data already loaded) would destroy/recreate
  // every uPlot instance for no data-related reason.
  const revenueChart = useMemo(() => {
    const days = [...data.by_day].reverse();
    return { labels: days.map(d => fmtDayShort(d.day)), series: [{ name: 'Umsatz (€)', values: days.map(d => d.paid_cents / 100) }] };
  }, [data.by_day]);
  const txChart = useMemo(() => {
    const days = [...data.by_day].reverse();
    return { labels: days.map(d => fmtDayShort(d.day)), series: [{ name: 'Transaktionen', values: days.map(d => d.tx_count) }] };
  }, [data.by_day]);

  // #45's two hourly readings — see the by_hour/by_hour_of_day comment on
  // computeStats() server-side for why they're separate. Revenue only
  // here (not also a tx-count variant like the per-day charts above) to
  // keep the page from growing a chart per metric per granularity; ask if
  // you want tx-count broken out hourly too.
  const hourlyRevenueChart = useMemo(
    () => ({
      labels: data.by_hour.map(h => fmtHour(h.hour)),
      series: [{ name: 'Umsatz (€)', values: data.by_hour.map(h => h.paid_cents / 100) }],
    }),
    [data.by_hour]
  );
  const hourOfDayRevenueChart = useMemo(
    () => ({
      labels: data.by_hour_of_day.map(h => `${String(h.hour_of_day).padStart(2, '0')}h`),
      series: [{ name: 'Umsatz (€)', values: data.by_hour_of_day.map(h => h.paid_cents / 100) }],
    }),
    [data.by_hour_of_day]
  );

  // Top 5 drinks by all-time volume — a per-drink-per-day line chart with
  // every drink on it gets unreadable past a handful of series, and the
  // long tail is rarely what "how's it developing" is actually asking
  // about.
  const topDrinkIds = useMemo(
    () => data.per_drink.slice(0, 5).map(d => d.drink_id),
    [data.per_drink]
  );
  const drinkTrend = useMemo(() => {
    const names = new Map(data.per_drink.map(d => [d.drink_id, d.drink_name]));
    const labels = data.per_drink_by_day.map(d => fmtDayShort(d.day));
    const series = topDrinkIds.map(id => ({
      name: names.get(id) ?? `#${id}`,
      values: data.per_drink_by_day.map(day => day.drinks.find(x => x.drink_id === id)?.sold_qty ?? 0),
    }));
    return { labels, series };
  }, [data.per_drink_by_day, topDrinkIds]);

  return (
    <div class="admin">
      <div class="row" style="justify-content: space-between">
        <h1 style="margin:0">Statistik</h1>
        <div class="row" style="gap: 8px">
          {isAdmin && <button class="danger" onClick={reset}>Statistik zurücksetzen</button>}
          <a href="/admin">← Backoffice</a>
        </div>
      </div>

      <h2>Umsatz pro Tag</h2>
      {revenueChart.labels.length === 0
        ? <p class="muted">Noch keine Verkäufe</p>
        : <DayChart labels={revenueChart.labels} series={revenueChart.series} />}

      <h2>Transaktionen pro Tag</h2>
      {txChart.labels.length === 0
        ? <p class="muted">Noch keine Verkäufe</p>
        : <DayChart labels={txChart.labels} series={txChart.series} />}

      <h2>Umsatz nach Stunde</h2>
      {hourlyRevenueChart.labels.length === 0
        ? <p class="muted">Noch keine Verkäufe</p>
        : <DayChart labels={hourlyRevenueChart.labels} series={hourlyRevenueChart.series} />}

      <h2>Umsatz nach Tageszeit</h2>
      {hourOfDayRevenueChart.series[0]!.values.every(v => v === 0)
        ? <p class="muted">Noch keine Verkäufe</p>
        : <DayChart labels={hourOfDayRevenueChart.labels} series={hourOfDayRevenueChart.series} />}

      <h2>Top-Getränke im Verlauf</h2>
      {drinkTrend.series.length === 0
        ? <p class="muted">Noch keine Verkäufe</p>
        : <DayChart labels={drinkTrend.labels} series={drinkTrend.series} />}

      <h2>Umsatz nach Tagen</h2>
      <table>
        <thead><tr><th>Tag</th><th>Transaktionen</th><th>Bezahlt</th><th>Crew-Transaktionen</th><th>Pfand zurück</th></tr></thead>
        <tbody>
          {data.by_day.length === 0 && <tr><td colSpan={5} class="muted">Noch keine Verkäufe</td></tr>}
          {data.by_day.map(d => (
            <tr key={d.day}>
              <td>{fmtDay(d.day)}</td>
              <td>{d.tx_count}</td>
              <td>{formatCents(d.paid_cents)}</td>
              <td>{d.crew_count}</td>
              <td>{d.pfand_returns}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <h2>Umsatz pro Bar</h2>
      <table>
        <thead><tr><th>Bar</th><th>Transaktionen</th><th>Bezahlt</th><th>Crew-Transaktionen</th><th>Pfand zurück</th></tr></thead>
        <tbody>
          {data.totals.map(t => (
            <tr key={t.bar_id}>
              <td>{t.bar_name}</td>
              <td>{t.tx_count}</td>
              <td>{formatCents(t.paid_cents)}</td>
              <td>{t.crew_count}</td>
              <td>{t.pfand_returns}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <h2>Getränke (gesamt)</h2>
      <table>
        <thead><tr><th>Getränk</th><th>Verkauft</th></tr></thead>
        <tbody>
          {data.per_drink.map(d => (
            <tr key={d.drink_id}>
              <td>{d.drink_name}</td>
              <td>{d.sold_qty ?? 0}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}
