import { useEffect, useState } from 'react';
import { Link } from '@tanstack/react-router';
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, rotateInvite, removeCloudMember, getUsage, type CloudCampaignInfo, type CloudCharInfo } 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; quota: number; admin: boolean } | null>(null);

  useEffect(() => { if (signedIn) listCloudCampaigns().then(setList).catch(() => {}); }, [signedIn]);
  useEffect(() => { if (signedIn) getUsage().then(setUsage).catch(() => {}); }, [signedIn]);
  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 smallcaps">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="smallcaps">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' && c.inviteCode && (
                  <Button size="sm" variant="ghost" title="Invalidate the old code and mint a new one" onClick={run(async () => { const r = await rotateInvite(c.id); setMsg(`New invite code: ${r.inviteCode}`); refresh(); })}>Rotate invite</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 items-center justify-between gap-2">
                      <span className="text-ink">{p.name}</span>
                      <span className="flex items-center gap-2">
                        <span className="text-muted">{p.mine ? 'yours' : 'a player’s'}</span>
                        {!p.mine && (
                          <button className="text-danger hover:underline" title="Remove this player and their characters from the campaign"
                            onClick={run(async () => { await removeCloudMember(c.id, p.ownerUserId); setMsg('Player removed.'); setParty(await listCloudCharacters(c.id)); })}>remove</button>
                        )}
                      </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 && (() => {
        const pct = usage.quota > 0 ? Math.min(100, Math.round((usage.bytes / usage.quota) * 100)) : 0;
        const near = pct >= 90;
        return (
          <div className="text-xs">
            <p className={near ? 'text-warning' : 'text-muted'}>
              Your cloud storage: <span className="text-ink">{fmtBytes(usage.bytes)}</span> of {fmtBytes(usage.quota)} ({pct}%)
              {near && ' — almost full; trim old data or ask the admin to raise your quota.'}
            </p>
            <div className="mt-1 h-1.5 w-full max-w-xs overflow-hidden rounded-full bg-elevated">
              <div className={near ? 'h-full bg-warning' : 'h-full bg-accent'} style={{ width: `${pct}%` }} />
            </div>
          </div>
        );
      })()}

      {usage?.admin && (
        <div className="rounded-md border border-accent/30 bg-accent/5 p-2 text-sm">
          You’re the instance admin — manage accounts, storage, campaigns, and live rooms in the{' '}
          <Link to="/admin" className="text-accent underline">Admin panel</Link>.
        </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`;
}
