import { useEffect, useRef, useState } from "react";
import { api } from "../api/client";
import type { AuthResponse, SyncStatus } from "../types/api";

function syncProgressLabel(status: SyncStatus): string {
  const { detail_fill_progress: fill, activities_pending_details: pending } = status;
  if (fill.Total > 0) {
    // pending includes the current batch, so subtract what's already
    // counted in this batch's Total to avoid double-counting the "beyond
    // this batch" remainder.
    const beyondBatch = Math.max(0, pending - (fill.Total - fill.Done));
    return `syncing: ${fill.Done}/${fill.Total} activities${beyondBatch > 0 ? ` (+${beyondBatch} more queued)` : ""}`;
  }
  return "syncing...";
}

export function GarminConnection({ onBeforeConnect }: { onBeforeConnect?: () => Promise<void> }) {
  const [auth, setAuth] = useState<AuthResponse | null>(null);
  const [code, setCode] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [syncStatus, setSyncStatus] = useState<SyncStatus | null>(null);
  // There's no real Garmin logout -- the backend session just sits idle.
  // "Disconnect" only hides that and shows Connect again locally; a real
  // login (connect()) clears it.
  const [disconnected, setDisconnected] = useState(false);

  function refreshStatus() {
    api.authStatus().then(setAuth).catch((e) => setError(String(e)));
    api.syncStatus().then(setSyncStatus).catch(() => {});
  }

  // Poll faster while a sync is actually running, so progress feels live;
  // back off to a relaxed interval the rest of the time.
  const syncStatusRef = useRef(syncStatus);
  syncStatusRef.current = syncStatus;

  useEffect(() => {
    refreshStatus();
    let timeout: ReturnType<typeof setTimeout>;
    const tick = () => {
      refreshStatus();
      timeout = setTimeout(tick, syncStatusRef.current?.in_progress ? 1500 : 6000);
    };
    timeout = setTimeout(tick, 1500);
    return () => clearTimeout(timeout);
  }, []);

  async function connect() {
    setBusy(true);
    setError(null);
    try {
      await onBeforeConnect?.();
      setAuth(await api.login());
      setDisconnected(false);
    } catch (e) {
      setError(String(e));
    } finally {
      setBusy(false);
    }
  }

  function disconnect() {
    setDisconnected(true);
  }

  async function submitMFA() {
    if (!code.trim()) return;
    setBusy(true);
    setError(null);
    try {
      setAuth(await api.submitMFA(code.trim()));
      setCode("");
    } catch (e) {
      setError(String(e));
    } finally {
      setBusy(false);
    }
  }

  async function sync() {
    setBusy(true);
    setError(null);
    try {
      await api.syncRun();
      refreshStatus();
    } catch (e) {
      setError(String(e));
    } finally {
      setBusy(false);
    }
  }

  async function resetAll() {
    if (!window.confirm("This deletes every synced activity, lap, and kind assignment, then starts a fresh pull from Garmin next sync. This cannot be undone. Continue?")) {
      return;
    }
    setBusy(true);
    setError(null);
    try {
      await api.resetSync();
      // Reset runs as a background sync job; wait for it to actually finish
      // before reloading, otherwise other pages (e.g. Activities) would
      // still show the just-deleted activities from their own stale state.
      let status = await api.syncStatus();
      while (status.in_progress) {
        await new Promise((resolve) => setTimeout(resolve, 300));
        status = await api.syncStatus();
      }
      window.location.reload();
    } catch (e) {
      setError(String(e));
      setBusy(false);
    }
  }

  const status = disconnected ? "unknown" : auth?.status ?? "unknown";

  return (
    <div className="garmin-connection">
      <div className="garmin-connection-row garmin-connection-main">
        <div className="garmin-connection-actions">
          {status !== "authenticated" && status !== "mfa_required" && (
            <button disabled={busy} onClick={connect}>
              Connect to Garmin
            </button>
          )}

          {status === "authenticated" && (
            <>
              <button disabled={busy} onClick={sync}>
                Sync now
              </button>
              <button disabled={busy} onClick={disconnect}>
                Disconnect
              </button>
            </>
          )}

          {/* Reset all only wipes local DB state (see handleSyncReset) -- it
              doesn't touch the Garmin session, so it stays available even
              while disconnected/not-yet-connected. */}
          <button className="button-danger" disabled={busy} onClick={resetAll}>
            Reset all
          </button>
        </div>

        <div className="garmin-connection-status">
          <span className={`status-dot status-${status}`} />
          {/* Connected/not-connected is already conveyed by the
              Connect/Disconnect button itself -- only MFA/failed need a
              label, since no button distinguishes those from "not connected". */}
          {(status === "mfa_required" || status === "failed") && (
            <span className="status-label">
              {status === "mfa_required" ? "MFA code required" : "Connection failed"}
            </span>
          )}
        </div>
      </div>

      {status === "mfa_required" && (
        <div className="garmin-connection-row">
          <input
            placeholder="MFA code"
            value={code}
            onChange={(e) => setCode(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && submitMFA()}
          />
          <button disabled={busy} onClick={submitMFA}>
            Submit code
          </button>
        </div>
      )}

      {!disconnected && auth?.message && <p className="garmin-connection-message">{auth.message}</p>}

      {/* Full-width so a long "last sync"/pending-details message has room
          to breathe, instead of being squeezed next to the status dot. */}
      {status === "authenticated" && syncStatus?.in_progress && (
        <p className="garmin-connection-message">{syncProgressLabel(syncStatus)}</p>
      )}
      {status === "authenticated" && syncStatus?.last_run && !syncStatus.in_progress && (
        <p className="garmin-connection-message">
          last sync: {syncStatus.last_run.Status} ({syncStatus.last_run.ActivitiesFetched} activities)
          {syncStatus.activities_pending_details > 0 &&
            ` — ${syncStatus.activities_pending_details} still need details, click Sync now again`}
        </p>
      )}

      {error && <p className="error">{error}</p>}
    </div>
  );
}
