import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { SyncStatus } from "@/components/SyncStatus";

describe("SyncStatus", () => {
  beforeEach(() => {
    // Mock Date.now for consistent time-based tests
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-02-26T12:00:00Z"));
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe("synced state", () => {
    it("shows green indicator when synced", () => {
      const lastSync = new Date("2026-02-26T11:58:00Z"); // 2 minutes ago
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      const indicator = screen.getByTestId("sync-indicator");
      expect(indicator).toHaveClass("bg-green-500");
    });

    it("shows relative time for recent sync", () => {
      const lastSync = new Date("2026-02-26T11:58:00Z"); // 2 minutes ago
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      expect(screen.getByText(/synced 2m ago/i)).toBeInTheDocument();
    });

    it("shows seconds for very recent sync", () => {
      const lastSync = new Date("2026-02-26T11:59:30Z"); // 30 seconds ago
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      expect(screen.getByText(/synced.*30s ago/i)).toBeInTheDocument();
    });

    it("shows 'just now' for immediate sync", () => {
      const lastSync = new Date("2026-02-26T12:00:00Z"); // right now
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      expect(screen.getByText(/synced just now/i)).toBeInTheDocument();
    });
  });

  describe("syncing state", () => {
    it("shows spinner when syncing", () => {
      render(<SyncStatus status="syncing" />);

      expect(screen.getByTestId("sync-spinner")).toBeInTheDocument();
    });

    it("shows 'Syncing...' text", () => {
      render(<SyncStatus status="syncing" />);

      expect(screen.getByText("Syncing...")).toBeInTheDocument();
    });

    it("does not show indicator dot when syncing", () => {
      render(<SyncStatus status="syncing" />);

      expect(screen.queryByTestId("sync-indicator")).not.toBeInTheDocument();
    });
  });

  describe("stale state", () => {
    it("shows amber indicator when explicitly stale", () => {
      const lastSync = new Date("2026-02-26T11:40:00Z"); // 20 minutes ago
      render(<SyncStatus status="stale" lastSync={lastSync} />);

      const indicator = screen.getByTestId("sync-indicator");
      expect(indicator).toHaveClass("bg-amber-500");
    });

    it("auto-detects stale when synced status but time > 15 minutes", () => {
      const lastSync = new Date("2026-02-26T11:40:00Z"); // 20 minutes ago
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      // Should show amber (stale) even though status is "synced"
      const indicator = screen.getByTestId("sync-indicator");
      expect(indicator).toHaveClass("bg-amber-500");
    });

    it("shows 'Last sync' text for stale", () => {
      const lastSync = new Date("2026-02-26T11:40:00Z"); // 20 minutes ago
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      expect(screen.getByText(/last sync 20m ago/i)).toBeInTheDocument();
    });

    it("shows refresh button when stale", () => {
      const lastSync = new Date("2026-02-26T11:40:00Z");
      render(<SyncStatus status="synced" lastSync={lastSync} onRetry={() => {}} />);

      expect(screen.getByRole("button", { name: /refresh/i })).toBeInTheDocument();
    });
  });

  describe("error state", () => {
    it("shows red indicator for error", () => {
      render(<SyncStatus status="error" error="lore command failed" />);

      const indicator = screen.getByTestId("sync-indicator");
      expect(indicator).toHaveClass("bg-red-500");
    });

    it("shows error message", () => {
      render(<SyncStatus status="error" error="lore command failed" />);

      expect(screen.getByText(/lore command failed/)).toBeInTheDocument();
    });

    it("shows retry button", () => {
      render(<SyncStatus status="error" error="failed" onRetry={() => {}} />);

      expect(screen.getByRole("button", { name: /retry/i })).toBeInTheDocument();
    });

    it("calls onRetry when retry button clicked", async () => {
      // Use real timers for this test since userEvent needs them
      vi.useRealTimers();
      const user = userEvent.setup();
      const onRetry = vi.fn();
      render(<SyncStatus status="error" error="failed" onRetry={onRetry} />);

      await user.click(screen.getByRole("button", { name: /retry/i }));

      expect(onRetry).toHaveBeenCalledTimes(1);
      // Restore fake timers for subsequent tests
      vi.useFakeTimers();
      vi.setSystemTime(new Date("2026-02-26T12:00:00Z"));
    });

    it("shows generic message when no error provided", () => {
      render(<SyncStatus status="error" />);

      expect(screen.getByText(/sync failed/i)).toBeInTheDocument();
    });
  });

  describe("offline state", () => {
    it("shows gray indicator when offline", () => {
      render(<SyncStatus status="offline" />);

      const indicator = screen.getByTestId("sync-indicator");
      expect(indicator).toHaveClass("bg-gray-400");
    });

    it("shows 'lore unavailable' message", () => {
      render(<SyncStatus status="offline" />);

      expect(screen.getByText(/lore unavailable/i)).toBeInTheDocument();
    });

    it("does not show retry button when offline", () => {
      render(<SyncStatus status="offline" onRetry={() => {}} />);

      // Offline doesn't get a retry button (user needs to fix lore first)
      expect(screen.queryByRole("button", { name: /retry/i })).not.toBeInTheDocument();
    });
  });

  describe("relative time formatting", () => {
    it("formats hours correctly", () => {
      const lastSync = new Date("2026-02-26T10:00:00Z"); // 2 hours ago
      render(<SyncStatus status="stale" lastSync={lastSync} />);

      expect(screen.getByText(/2h ago/i)).toBeInTheDocument();
    });

    it("formats days correctly", () => {
      const lastSync = new Date("2026-02-24T12:00:00Z"); // 2 days ago
      render(<SyncStatus status="stale" lastSync={lastSync} />);

      expect(screen.getByText(/2d ago/i)).toBeInTheDocument();
    });
  });

  describe("no onRetry callback", () => {
    it("hides retry button when no onRetry provided for error", () => {
      render(<SyncStatus status="error" error="failed" />);

      expect(screen.queryByRole("button", { name: /retry/i })).not.toBeInTheDocument();
    });

    it("hides refresh button when no onRetry provided for stale", () => {
      const lastSync = new Date("2026-02-26T11:40:00Z");
      render(<SyncStatus status="synced" lastSync={lastSync} />);

      expect(screen.queryByRole("button")).not.toBeInTheDocument();
    });
  });
});
