// ABOUTME: Login page for user authentication.
// ABOUTME: Provides OIDC (Pocket-ID) login with email/password fallback.
"use client";

import { useRouter } from "next/navigation";
import { type FormEvent, useCallback, useEffect, useState } from "react";

import { pb } from "@/lib/pocketbase";

interface AuthProvider {
  name: string;
  displayName: string;
  state: string;
  codeVerifier: string;
  codeChallenge: string;
  codeChallengeMethod: string;
  authURL: string;
}

const RATE_LIMIT_MAX_ATTEMPTS = 5;
const RATE_LIMIT_WINDOW_MS = 60000; // 1 minute

export default function LoginPage() {
  const router = useRouter();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [isCheckingAuth, setIsCheckingAuth] = useState(true);
  const [oidcProvider, setOidcProvider] = useState<AuthProvider | null>(null);
  const [loginAttempts, setLoginAttempts] = useState<number[]>([]);
  const [isRateLimited, setIsRateLimited] = useState(false);

  // Get recent attempts within the rate limit window
  const getRecentAttempts = useCallback(() => {
    const now = Date.now();
    return loginAttempts.filter(
      (timestamp) => now - timestamp < RATE_LIMIT_WINDOW_MS,
    );
  }, [loginAttempts]);

  // Calculate remaining attempts
  const getRemainingAttempts = useCallback(() => {
    const recentAttempts = getRecentAttempts();
    return Math.max(0, RATE_LIMIT_MAX_ATTEMPTS - recentAttempts.length);
  }, [getRecentAttempts]);

  // Check if rate limited
  const checkRateLimited = useCallback(() => {
    const recentAttempts = getRecentAttempts();
    return recentAttempts.length >= RATE_LIMIT_MAX_ATTEMPTS;
  }, [getRecentAttempts]);

  // Record a login attempt
  const recordAttempt = useCallback(() => {
    const now = Date.now();
    setLoginAttempts((prev) => [
      ...prev.filter((t) => now - t < RATE_LIMIT_WINDOW_MS),
      now,
    ]);
  }, []);

  // Check available auth methods on mount
  useEffect(() => {
    const checkAuthMethods = async () => {
      try {
        const authMethods = await pb.collection("users").listAuthMethods();
        const oidc = authMethods.oauth2?.providers?.find(
          (p: AuthProvider) => p.name === "oidc",
        );
        if (oidc) {
          setOidcProvider(oidc);
        }
      } catch {
        // If listAuthMethods fails, fall back to email/password
      } finally {
        setIsCheckingAuth(false);
      }
    };
    checkAuthMethods();
  }, []);

  // Check rate limit status and set up cooldown timer
  useEffect(() => {
    if (checkRateLimited()) {
      setIsRateLimited(true);
      setError("Too many login attempts. Please try again in 1 minute.");

      // Set a timer to clear rate limit after window expires
      const oldestAttempt = loginAttempts[0];
      const timeUntilReset = oldestAttempt
        ? RATE_LIMIT_WINDOW_MS - (Date.now() - oldestAttempt)
        : RATE_LIMIT_WINDOW_MS;

      const timer = setTimeout(
        () => {
          setIsRateLimited(false);
          setError(null);
          // Clean up old attempts
          setLoginAttempts((prev) =>
            prev.filter((t) => Date.now() - t < RATE_LIMIT_WINDOW_MS),
          );
        },
        Math.max(0, timeUntilReset),
      );

      return () => clearTimeout(timer);
    }
    setIsRateLimited(false);
  }, [loginAttempts, checkRateLimited]);

  const handleOidcLogin = async () => {
    // Check rate limit before attempting
    if (checkRateLimited()) {
      setError("Too many login attempts. Please try again in 1 minute.");
      return;
    }

    setIsLoading(true);
    setError(null);

    try {
      await pb.collection("users").authWithOAuth2({ provider: "oidc" });
      // Reset attempts on successful login
      setLoginAttempts([]);
      router.push("/");
    } catch (err) {
      // Record the failed attempt
      recordAttempt();
      const message =
        err instanceof Error ? err.message : "Authentication failed";
      const remaining = getRemainingAttempts() - 1; // -1 because we just recorded an attempt
      if (remaining > 0 && remaining <= 2) {
        setError(
          `${message}. ${remaining} attempt${remaining === 1 ? "" : "s"} remaining.`,
        );
      } else {
        setError(message);
      }
      setIsLoading(false);
    }
  };

  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    // Basic validation - don't submit with empty fields
    if (!email.trim() || !password.trim()) {
      return;
    }

    // Check rate limit before attempting
    if (checkRateLimited()) {
      setError("Too many login attempts. Please try again in 1 minute.");
      return;
    }

    setIsLoading(true);
    setError(null);

    try {
      await pb.collection("users").authWithPassword(email, password);
      // Reset attempts on successful login
      setLoginAttempts([]);
      router.push("/");
    } catch (err) {
      // Record the failed attempt
      recordAttempt();
      const message =
        err instanceof Error ? err.message : "Invalid credentials";
      const remaining = getRemainingAttempts() - 1; // -1 because we just recorded an attempt
      if (remaining > 0 && remaining <= 2) {
        setError(
          `${message}. ${remaining} attempt${remaining === 1 ? "" : "s"} remaining.`,
        );
      } else {
        setError(message);
      }
      setIsLoading(false);
    }
  };

  const handleInputChange = (
    setter: React.Dispatch<React.SetStateAction<string>>,
    value: string,
  ) => {
    setter(value);
    // Clear error when user starts typing again (but not rate limit errors)
    if (error && !isRateLimited) {
      setError(null);
    }
  };

  // Show loading state while checking auth methods
  if (isCheckingAuth) {
    return (
      <main
        id="main-content"
        className="flex min-h-screen items-center justify-center"
      >
        <div className="w-full max-w-md space-y-8 p-8">
          <h1 className="text-2xl font-bold text-center">PhaseFlow</h1>
          <div className="text-center text-gray-500">Loading...</div>
        </div>
      </main>
    );
  }

  return (
    <main
      id="main-content"
      className="flex min-h-screen items-center justify-center"
    >
      <div className="w-full max-w-md space-y-8 p-8">
        <h1 className="text-2xl font-bold text-center">PhaseFlow</h1>

        {error && (
          <div
            role="alert"
            className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded"
          >
            {error}
          </div>
        )}

        {oidcProvider ? (
          // OIDC login button
          <button
            type="button"
            onClick={handleOidcLogin}
            disabled={isLoading || isRateLimited}
            className="w-full rounded-md bg-blue-600 px-4 py-2 text-white font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-400 disabled:cursor-not-allowed"
          >
            {isLoading
              ? "Signing in..."
              : `Sign in with ${oidcProvider.displayName}`}
          </button>
        ) : (
          // Email/password form fallback
          <form onSubmit={handleSubmit} className="space-y-6">
            <div>
              <label
                htmlFor="email"
                className="block text-sm font-medium text-gray-700"
              >
                Email
              </label>
              <input
                id="email"
                type="email"
                value={email}
                onChange={(e) => handleInputChange(setEmail, e.target.value)}
                disabled={isLoading}
                className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
                required
              />
            </div>

            <div>
              <label
                htmlFor="password"
                className="block text-sm font-medium text-gray-700"
              >
                Password
              </label>
              <input
                id="password"
                type="password"
                value={password}
                onChange={(e) => handleInputChange(setPassword, e.target.value)}
                disabled={isLoading}
                className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
                required
              />
            </div>

            <button
              type="submit"
              disabled={isLoading || isRateLimited}
              className="w-full rounded-md bg-blue-600 px-4 py-2 text-white font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-400 disabled:cursor-not-allowed"
            >
              {isLoading ? "Signing in..." : "Sign in"}
            </button>
          </form>
        )}
      </div>
    </main>
  );
}
