import { useEffect, useRef, useState } from "react";

type ProgressStatus = "queued" | "running" | "submitting" | "completed" | "failed" | "success" | "error";

interface SmoothedProgressOptions {
  creepSpeed?: number;
  chaseRate?: number;
  creepCeiling?: number;
  creepAhead?: number;
  completionDuration?: number;
}

const DEFAULT_CREEP_SPEED = 0.5;
const DEFAULT_CHASE_RATE = 0.09;
const DEFAULT_CREEP_CEILING = 97;
const DEFAULT_CREEP_AHEAD = 25;
const DEFAULT_COMPLETION_DURATION = 450;

function isTerminal(status: ProgressStatus): boolean {
  return status === "completed" || status === "success" || status === "failed" || status === "error";
}

function isSuccess(status: ProgressStatus): boolean {
  return status === "completed" || status === "success";
}

/**
 * Smoothly interpolates between coarse server-reported progress values.
 * On completion, animates quickly to 100% instead of jumping.
 */
export function useSmoothedProgress(
  realProgress: number,
  status: ProgressStatus,
  options?: SmoothedProgressOptions,
): number {
  const creepSpeed = options?.creepSpeed ?? DEFAULT_CREEP_SPEED;
  const chaseRate = options?.chaseRate ?? DEFAULT_CHASE_RATE;
  const creepCeiling = options?.creepCeiling ?? DEFAULT_CREEP_CEILING;
  const creepAhead = options?.creepAhead ?? DEFAULT_CREEP_AHEAD;
  const completionDuration = options?.completionDuration ?? DEFAULT_COMPLETION_DURATION;

  const [displayed, setDisplayed] = useState(0);
  const rafRef = useRef(0);
  const targetRef = useRef(realProgress);
  const statusRef = useRef(status);
  const completionStartRef = useRef<number | null>(null);
  const completionBaseRef = useRef(0);

  useEffect(() => {
    targetRef.current = realProgress;
    statusRef.current = status;

    if (isSuccess(status) && completionStartRef.current === null) {
      completionStartRef.current = performance.now();
      completionBaseRef.current = displayed;
    }
  }, [realProgress, status]);

  useEffect(() => {
    if (status === "failed" || status === "error") {
      cancelAnimationFrame(rafRef.current);
      return;
    }

    let lastFrame = performance.now();

    const tick = (now: number) => {
      const dt = Math.min((now - lastFrame) / 1000, 0.1);
      lastFrame = now;

      setDisplayed((current) => {
        const currentStatus = statusRef.current;

        if (isSuccess(currentStatus)) {
          const start = completionStartRef.current ?? now;
          const elapsed = now - start;
          const t = Math.min(elapsed / completionDuration, 1);
          const eased = 1 - (1 - t) * (1 - t);
          const base = completionBaseRef.current;
          return base + (100 - base) * eased;
        }

        if (isTerminal(currentStatus)) return current;

        const target = targetRef.current;

        if (current >= target) {
          const ceiling = Math.min(target + creepAhead, creepCeiling);
          if (current >= ceiling) return current;
          return current + creepSpeed * dt;
        }

        const gap = target - current;
        const step = (gap * chaseRate + 0.3) * dt * 60;
        return Math.min(current + step, target);
      });

      rafRef.current = requestAnimationFrame(tick);
    };

    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [status, chaseRate, creepSpeed, creepCeiling, creepAhead, completionDuration]);

  if (status === "failed" || status === "error") return Math.round(displayed);
  if (isSuccess(status) && displayed >= 99.5) return 100;
  return Math.round(Math.max(0, Math.min(99, displayed)));
}
