import { useEffect, useState } from 'react';
import { useLiveQuery } from 'dexie-react-hooks';
import { db } from '@/lib/db/db';
import { cloudUsername, CloudError } from '@/lib/cloud/client';
import { listCloudCampaigns, createCloudCampaign, joinCloudCampaign, publishCharacter, listCloudCharacters, getUsage, adminListUsers, adminSetQuota, type CloudCampaignInfo, type CloudCharInfo, type AdminUserRow } from '@/lib/cloud/campaigns';
import { useCampaigns } from '@/features/campaigns/hooks';
import { Button } from '@/components/ui/Button';
import { Input, Select } from '@/components/ui/Input';

/** Settings: publish a campaign for players, join one, and publish your character (it stays yours + syncs). */
export function CloudCampaigns() {
  const signedIn = !!cloudUsername();
  const [list, setList] = useState<CloudCampaignInfo[]>([]);
  const [msg, setMsg] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const localCampaigns = useCampaigns();
  const localChars = useLiveQuery(() => db.characters.toArray(), [], []);
  const [invite, setInvite] = useState('');
  const [pubCampaign, setPubCampaign] = useState('');
  const [charId, setCharId] = useState('');
  const [targetCloud, setTargetCloud] = useState('');
  const [viewing, setViewing] = useState<string | null>(null);
  const [party, setParty] = useState<CloudCharInfo[]>([]);
  const [usage, setUsage] = useState<{ bytes: number; admin: boolean } | null>(null);
  const [admins, setAdmins] = useState<AdminUserRow[]>([]);

  useEffect(() => { if (signedIn) listCloudCampaigns().then(setList).catch(() => {}); }, [signedIn]);
  useEffect(() => { if (signedIn) getUsage().then(setUsage).catch(() => {}); }, [signedIn]);
  const loadAdmin = () => adminListUsers().then((r) => setAdmins(r.users)).catch(() => {});
  useEffect(() => { if (usage?.admin) loadAdmin(); }, [usage?.admin]);
  const refresh = () => listCloudCampaigns().then(setList).catch(() => {});

  const run = (fn: () => Promise<void>) => async () => {
    setBusy(true); setMsg(null);
    try { await fn(); } catch (e) { setMsg(e instanceof CloudError ? e.message : 'Something went wrong.'); } finally { setBusy(false); }
  };

  if (!signedIn) {
    return (
      <section className="rounded-lg border border-line bg-panel p-4">
        <h2 className="mb-2 text-xs font-semibold uppercase tracking-wide text-muted">Shared campaigns (cloud)</h2>
        <p className="text-sm text-muted">Sign in above to publish a campaign for your players, or join one and publish your character — it stays yours and syncs when you edit it.</p>
      </section>
    );
  }

  const owned = list.filter((c) => c.role === 'owner');

  return (
    <section className="space-y-4 rounded-lg border border-line bg-panel p-4">
      <h2 className="text-xs font-semibold uppercase tracking-wide text-muted">Shared campaigns (cloud)</h2>

      {list.length > 0 && (
        <ul className="space-y-2">
          {list.map((c) => (
            <li key={c.id} className="rounded-md border border-line bg-surface p-2 text-sm">
              <div className="flex flex-wrap items-center gap-2">
                <span className="font-medium text-ink">{c.name}</span>
                <span className="rounded-full bg-elevated px-1.5 py-0.5 text-[10px] uppercase text-muted">{c.role}</span>
                {c.inviteCode && (
                  <button className="font-mono text-xs text-accent" title="Copy invite code" onClick={() => void navigator.clipboard?.writeText(c.inviteCode!).then(() => setMsg(`Invite code ${c.inviteCode} copied.`))}>invite: {c.inviteCode}</button>
                )}
                {c.role === 'owner' && <Button size="sm" variant="ghost" onClick={run(async () => { setViewing(c.id); setParty(await listCloudCharacters(c.id)); })}>View party</Button>}
              </div>
              {viewing === c.id && (
                <ul className="mt-1 space-y-0.5 border-t border-line pt-1 text-xs">
                  {party.length === 0 ? <li className="text-muted">No characters published yet.</li> : party.map((p) => (
                    <li key={p.id} className="flex justify-between"><span className="text-ink">{p.name}</span><span className="text-muted">{p.mine ? 'yours' : 'a player’s'}</span></li>
                  ))}
                </ul>
              )}
            </li>
          ))}
        </ul>
      )}

      <div className="grid gap-3 sm:grid-cols-2">
        <div>
          <h3 className="mb-1 text-xs font-medium text-muted">Publish a campaign (you = GM)</h3>
          <div className="flex gap-1">
            <Select value={pubCampaign} onChange={(e) => setPubCampaign(e.target.value)} aria-label="Local campaign to publish" className="flex-1">
              <option value="">Choose a campaign…</option>
              {localCampaigns.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
            </Select>
            <Button size="sm" variant="primary" disabled={busy || !pubCampaign} onClick={run(async () => { const c = localCampaigns.find((x) => x.id === pubCampaign); if (!c) return; await createCloudCampaign(c.name, c.system); setMsg(`Published “${c.name}” — share its invite code.`); refresh(); })}>Publish</Button>
          </div>
        </div>

        <div>
          <h3 className="mb-1 text-xs font-medium text-muted">Join a campaign (you = player)</h3>
          <div className="flex gap-1">
            <Input value={invite} onChange={(e) => setInvite(e.target.value.toUpperCase())} placeholder="Invite code" aria-label="Invite code" className="flex-1" />
            <Button size="sm" variant="secondary" disabled={busy || !invite.trim()} onClick={run(async () => { const c = await joinCloudCampaign(invite.trim()); setInvite(''); setMsg(`Joined “${c.name}”.`); refresh(); })}>Join</Button>
          </div>
        </div>
      </div>

      <div>
        <h3 className="mb-1 text-xs font-medium text-muted">Publish a character (stays yours, syncs on edit)</h3>
        <div className="flex flex-wrap gap-1">
          <Select value={charId} onChange={(e) => setCharId(e.target.value)} aria-label="Character to publish">
            <option value="">Choose a character…</option>
            {(localChars ?? []).map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
          </Select>
          <Select value={targetCloud} onChange={(e) => setTargetCloud(e.target.value)} aria-label="Target shared campaign">
            <option value="">…to which campaign</option>
            {list.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
          </Select>
          <Button size="sm" variant="primary" disabled={busy || !charId || !targetCloud} onClick={run(async () => { const ch = (localChars ?? []).find((c) => c.id === charId); if (!ch) return; await publishCharacter(targetCloud, { id: ch.id, name: ch.name, data: JSON.stringify(ch) }); setMsg(`Published ${ch.name}.`); if (viewing === targetCloud) setParty(await listCloudCharacters(targetCloud)); })}>Publish</Button>
        </div>
      </div>

      {usage && <p className="text-xs text-muted">Your cloud storage: <span className="text-ink">{fmtBytes(usage.bytes)}</span></p>}

      {usage?.admin && (
        <div className="rounded-md border border-accent/30 bg-accent/5 p-2">
          <h3 className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted">Admin · users &amp; storage</h3>
          <table className="w-full text-xs">
            <thead><tr className="text-left text-muted"><th className="py-1 font-medium">User</th><th className="font-medium">Used</th><th className="font-medium">Quota (GB, 0=default)</th></tr></thead>
            <tbody>
              {admins.map((u) => (
                <tr key={u.username} className="border-t border-line">
                  <td className="py-1 text-ink">{u.username}</td>
                  <td className="py-1 text-muted">{fmtBytes(u.usageBytes)}</td>
                  <td className="py-1"><QuotaCell user={u} onSaved={loadAdmin} /></td>
                </tr>
              ))}
            </tbody>
          </table>
          <p className="mt-1 text-[11px] text-muted">Quotas are tracked but not yet enforced — a safety valve for later.</p>
        </div>
      )}

      {msg && <p className="text-sm text-accent">{msg}</p>}
      <p className="text-xs text-muted">{owned.length > 0 ? 'Share an owner campaign’s invite code with your players. They sign in, join, and publish their characters — you see them under “View party”.' : 'Publish one of your campaigns to get an invite code for players.'}</p>
    </section>
  );
}

function fmtBytes(b: number): string {
  if (b >= 1e9) return `${(b / 1e9).toFixed(2)} GB`;
  if (b >= 1e6) return `${(b / 1e6).toFixed(1)} MB`;
  return `${Math.max(0, Math.round(b / 1024))} KB`;
}

function QuotaCell({ user, onSaved }: { user: AdminUserRow; onSaved: () => void }) {
  const [gb, setGb] = useState(String(user.quotaBytes ? (user.quotaBytes / 1e9).toFixed(0) : '0'));
  const [saving, setSaving] = useState(false);
  const save = async () => { setSaving(true); try { await adminSetQuota(user.username, (Number(gb) || 0) * 1e9); onSaved(); } catch { /* ignore */ } finally { setSaving(false); } };
  return (
    <span className="flex items-center gap-1">
      <input value={gb} onChange={(e) => setGb(e.target.value.replace(/[^0-9]/g, ''))} className="w-12 rounded border border-line bg-surface px-1 py-0.5 text-xs" aria-label={`Quota GB for ${user.username}`} />
      <Button size="sm" variant="ghost" disabled={saving} onClick={() => void save()}>Set</Button>
    </span>
  );
}
