import { useState, useEffect, useMemo } from 'react';
import { useClient } from 'hooks/useClient';
import { RefreshCw, X, Folder, ArrowUpFromLine } from 'lucide-react';
import { Card, CardHeader, CardBody, SubCard, SubCardHeader, RowList } from './Cards';
import {
  basename,
  folderLabel,
  formatSize,
  formatSpeed,
  transferPhase,
  type SlskdTransfer,
  type SlskdTransferUser,
  type TransferPhase,
} from './shared';

// Uploads panel — the mirror image of Downloads: what peers are pulling from us. GET /transfers/uploads
// returns the same user → directories → files shape, so we group and render it the same way (collapsible
// card per peer, files by folder, a progress bar per file). Basic version: one flat list, per-row
// cancel/clear; no retry/queue-position controls (those are download-side concerns).

const POLL_MS = 1500;

type Row = SlskdTransfer & { phase: TransferPhase };
type UpDir = { directory: string; label: string; files: Row[]; size: number };
type UpUser = { username: string; dirs: UpDir[]; counts: Record<TransferPhase, number>; total: number };

const PHASE_ORDER: Record<TransferPhase, number> = { downloading: 0, queued: 1, failed: 2, done: 3 };
const PHASES: TransferPhase[] = ['downloading', 'queued', 'failed', 'done'];

const phaseStyle: Record<TransferPhase, { label: string; dot: string; bar: string }> = {
  downloading: { label: 'Uploading', dot: 'bg-blue-500', bar: 'bg-blue-500' },
  queued: { label: 'Queued', dot: 'bg-amber-500', bar: 'bg-amber-400' },
  done: { label: 'Done', dot: 'bg-green-500', bar: 'bg-green-500' },
  failed: { label: 'Failed', dot: 'bg-red-500', bar: 'bg-red-500' },
};

const isRemovable = (phase: TransferPhase) => phase === 'done' || phase === 'failed';

const group = (users: SlskdTransferUser[]): UpUser[] => {
  const out: UpUser[] = [];
  for (const user of users) {
    const dirs: UpDir[] = [];
    for (const dir of user.directories ?? []) {
      const files: Row[] = (dir.files ?? []).map((f) => ({ ...f, phase: transferPhase(f.state) }));
      if (files.length === 0) continue;
      files.sort(
        (a, b) => PHASE_ORDER[a.phase] - PHASE_ORDER[b.phase] || basename(a.filename).localeCompare(basename(b.filename)),
      );
      dirs.push({
        directory: dir.directory,
        label: folderLabel(dir.directory),
        files,
        size: files.reduce((n, f) => n + f.size, 0),
      });
    }
    if (dirs.length === 0) continue;
    dirs.sort((a, b) => a.directory.localeCompare(b.directory));
    const counts: Record<TransferPhase, number> = { downloading: 0, queued: 0, failed: 0, done: 0 };
    let total = 0;
    for (const dir of dirs) for (const f of dir.files) (counts[f.phase]++, total++);
    out.push({ username: user.username, dirs, counts, total });
  }
  out.sort(
    (a, b) => Number(b.counts.downloading > 0) - Number(a.counts.downloading > 0) || a.username.localeCompare(b.username),
  );
  return out;
};

export const SoulseekUploads = () => {
  const client = useClient();
  const [data, setData] = useState<SlskdTransferUser[]>([]);
  const [loaded, setLoaded] = useState(false);
  const [collapsed, setCollapsed] = useState<Set<string>>(new Set());

  const users = useMemo(() => group(data), [data]);

  useEffect(() => {
    let cancelled = false;
    const tick = () =>
      client
        .get<SlskdTransferUser[]>('/slskd/api/v0/transfers/uploads')
        .then((u) => !cancelled && (setData(u), setLoaded(true)))
        .catch(() => {});
    tick();
    const timer = setInterval(tick, POLL_MS);
    return () => {
      cancelled = true;
      clearInterval(timer);
    };
    // useClient() is a fresh object each render — poll on a stable interval only.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const toggle = (name: string) =>
    setCollapsed((prev) => {
      const next = new Set(prev);
      if (next.has(name)) next.delete(name);
      else next.add(name);
      return next;
    });

  const dropIds = (ids: Set<string>) =>
    setData((prev) =>
      prev.map((u) => ({
        ...u,
        directories: (u.directories ?? []).map((d) => ({ ...d, files: (d.files ?? []).filter((f) => !ids.has(f.id)) })),
      })),
    );

  // Abort an in-flight upload, or clear a finished/failed one from the queue.
  const remove = async (row: Row) => {
    const done = isRemovable(row.phase);
    try {
      await client.delete(
        `/slskd/api/v0/transfers/uploads/${encodeURIComponent(row.username)}/${row.id}${done ? '?remove=true' : ''}`,
      );
      dropIds(new Set([row.id]));
    } catch {
      /* next poll reconciles */
    }
  };

  return (
    <div className="flex h-full flex-col overflow-hidden">
      <div className="flex shrink-0 items-center gap-2 border-b p-3 text-sm">
        <ArrowUpFromLine className="h-4 w-4 shrink-0 text-muted-foreground" />
        <span className="font-medium">Uploads</span>
        <span className="text-muted-foreground">· {users.length} peer{users.length === 1 ? '' : 's'}</span>
        {!loaded && <RefreshCw className="ml-auto h-3.5 w-3.5 animate-spin text-muted-foreground" />}
      </div>

      <div className="min-h-0 flex-1 overflow-y-auto p-3">
        {users.length === 0 ? (
          <p className="text-sm text-muted-foreground">
            {loaded ? 'No uploads. When a peer downloads a shared file it shows up here.' : 'Loading uploads…'}
          </p>
        ) : (
          <div className="flex flex-col gap-2">
            {users.map((user) => (
              <UserCard
                key={user.username}
                user={user}
                open={!collapsed.has(user.username)}
                onToggle={() => toggle(user.username)}
                onRemove={remove}
              />
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

type UserCardProps = { user: UpUser; open: boolean; onToggle: () => void; onRemove: (row: Row) => void };

const UserCard = ({ user, open, onToggle, onRemove }: UserCardProps) => {
  const meta = PHASES.filter((p) => user.counts[p] > 0).map((p) => (
    <span key={p} className="inline-flex items-center gap-1" title={phaseStyle[p].label}>
      <span className={`h-2 w-2 rounded-full ${phaseStyle[p].dot}`} />
      {user.counts[p]}
    </span>
  ));
  return (
    <Card>
      <CardHeader open={open} onToggle={onToggle} title={user.username} meta={meta} />
      {open && (
        <CardBody>
          {user.dirs.map((dir) => (
            <FolderBlock key={dir.directory} dir={dir} onRemove={onRemove} />
          ))}
        </CardBody>
      )}
    </Card>
  );
};

type FolderBlockProps = { dir: UpDir; onRemove: (row: Row) => void };

const FolderBlock = ({ dir, onRemove }: FolderBlockProps) => {
  const [open, setOpen] = useState(true);
  return (
    <SubCard>
      <SubCardHeader
        open={open}
        onToggle={() => setOpen((v) => !v)}
        icon={<Folder className="h-3.5 w-3.5 shrink-0 text-zinc-500" />}
        label={dir.label}
        title={dir.directory}
        meta={`${dir.files.length} · ${formatSize(dir.size)}`}
      />
      {open && (
        <RowList>
          {dir.files.map((row) => {
            const style = phaseStyle[row.phase];
            const pct = row.phase === 'done' ? 100 : Math.max(0, Math.min(100, Math.round(row.percentComplete)));
            const speed = row.phase === 'downloading' ? formatSpeed(row.averageSpeed) : '';
            return (
              <div key={row.id} className="px-3 py-2 pl-8 text-sm text-zinc-200">
                <div className="flex items-center gap-2">
                  <span className={`h-2 w-2 shrink-0 rounded-full ${style.dot}`} />
                  <span className="min-w-0 flex-1 truncate">{basename(row.filename)}</span>
                  <button
                    type="button"
                    onClick={() => onRemove(row)}
                    title={isRemovable(row.phase) ? 'Clear' : 'Cancel'}
                    className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-zinc-400 hover:bg-white/10 hover:text-zinc-100"
                  >
                    <X className="h-3.5 w-3.5" />
                  </button>
                </div>
                <div className="mt-1.5 h-1.5 w-full overflow-hidden rounded-full bg-white/10">
                  <div className={`h-full ${style.bar}`} style={{ width: `${pct}%` }} />
                </div>
                <div className="mt-1 flex flex-wrap items-center gap-x-2 text-xs text-zinc-500">
                  <span>{style.label}</span>
                  {row.size > 0 && <span>· {formatSize(row.size)}</span>}
                  {speed && <span>· {speed}</span>}
                </div>
              </div>
            );
          })}
        </RowList>
      )}
    </SubCard>
  );
};
