/**
 * ActiveProjectsWidget Component Tests
 * Following TDD principles
 */

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import { ActiveProjectsWidget } from "../ActiveProjectsWidget";
import userEvent from "@testing-library/user-event";
import { clearCsrfToken } from "@/lib/api/client";

// Mock fetch for API calls
global.fetch = vi.fn() as typeof global.fetch;

// Helper to create mock CSRF token response
const mockCsrfResponse = (): Response =>
  ({
    ok: true,
    json: () => Promise.resolve({ token: "test-csrf-token" }),
  }) as Response;

describe("ActiveProjectsWidget", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();
    clearCsrfToken(); // Clear cached CSRF token between tests
  });

  afterEach((): void => {
    vi.resetAllMocks();
  });

  it("should render loading state initially", (): void => {
    // First call returns CSRF token, subsequent calls never resolve (loading state)
    let csrfReturned = false;
    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token on first request
      if (urlString.includes("csrf") && !csrfReturned) {
        csrfReturned = true;
        return Promise.resolve(mockCsrfResponse());
      }
      // All other requests never resolve
      return new Promise(() => {
        // Intentionally empty - creates a never-resolving promise for loading state
      });
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    expect(screen.getByText(/loading projects/i)).toBeInTheDocument();
    expect(screen.getByText(/loading agents/i)).toBeInTheDocument();
  });

  it("should render active projects list", async (): Promise<void> => {
    const mockProjects = [
      {
        id: "1",
        name: "Website Redesign",
        status: "ACTIVE",
        lastActivity: new Date().toISOString(),
        taskCount: 12,
        eventCount: 3,
        color: "#3B82F6",
      },
      {
        id: "2",
        name: "Mobile App",
        status: "ACTIVE",
        lastActivity: new Date().toISOString(),
        taskCount: 8,
        eventCount: 1,
        color: "#10B981",
      },
    ];

    const mockAgentSessions: never[] = [];

    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("active-projects")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockProjects),
        } as Response);
      } else if (urlString.includes("agent-chains")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockAgentSessions),
        } as Response);
      }
      return Promise.reject(new Error("Unknown URL"));
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText("Website Redesign")).toBeInTheDocument();
      expect(screen.getByText("Mobile App")).toBeInTheDocument();
      expect(screen.getByText("12 tasks")).toBeInTheDocument();
      expect(screen.getByText("8 tasks")).toBeInTheDocument();
    });
  });

  it("should render agent sessions list", async (): Promise<void> => {
    const mockProjects: never[] = [];
    const mockAgentSessions = [
      {
        id: "1",
        sessionKey: "session-123",
        label: "Code Review Agent",
        channel: "slack",
        agentName: "ReviewBot",
        agentStatus: "WORKING",
        status: "active" as const,
        startedAt: new Date(Date.now() - 120000).toISOString(), // 2 minutes ago
        lastMessageAt: new Date().toISOString(),
        runtimeMs: 120000,
        messageCount: 5,
        contextSummary: "Reviewing PR #123",
      },
    ];

    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("active-projects")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockProjects),
        } as Response);
      } else if (urlString.includes("agent-chains")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockAgentSessions),
        } as Response);
      }
      return Promise.reject(new Error("Unknown URL"));
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText("Code Review Agent")).toBeInTheDocument();
      expect(screen.getByText("5 msgs")).toBeInTheDocument();
      expect(screen.getByText(/running/i)).toBeInTheDocument();
    });
  });

  it("should handle empty states", async (): Promise<void> => {
    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText(/no active projects/i)).toBeInTheDocument();
      expect(screen.getByText(/no running agents/i)).toBeInTheDocument();
    });
  });

  it("should expand agent session details on click", async (): Promise<void> => {
    const mockAgentSession = {
      id: "1",
      sessionKey: "session-123",
      label: "Test Agent",
      channel: null,
      agentName: null,
      agentStatus: "WORKING",
      status: "active" as const,
      startedAt: new Date().toISOString(),
      lastMessageAt: new Date().toISOString(),
      runtimeMs: 60000,
      messageCount: 3,
      contextSummary: "Working on task XYZ",
    };

    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("agent-chains")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve([mockAgentSession]),
        } as Response);
      }
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText("Test Agent")).toBeInTheDocument();
    });

    // Click to expand
    const agentElement = screen.getByText("Test Agent");
    await userEvent.click(agentElement);

    await waitFor(() => {
      expect(screen.getByText("Working on task XYZ")).toBeInTheDocument();
    });
  });

  it("should format runtime correctly", async (): Promise<void> => {
    const mockAgentSession = {
      id: "1",
      sessionKey: "session-123",
      label: "Test Agent",
      channel: null,
      agentName: null,
      agentStatus: "WORKING",
      status: "active" as const,
      startedAt: new Date(Date.now() - 3660000).toISOString(), // 1 hour 1 minute ago
      lastMessageAt: new Date().toISOString(),
      runtimeMs: 3660000, // 1h 1m
      messageCount: 10,
      contextSummary: null,
    };

    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("agent-chains")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve([mockAgentSession]),
        } as Response);
      }
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText(/1h 1m/)).toBeInTheDocument();
    });
  });

  it("should display project count badges", async (): Promise<void> => {
    const mockProjects = [
      {
        id: "1",
        name: "Project 1",
        status: "ACTIVE",
        lastActivity: new Date().toISOString(),
        taskCount: 5,
        eventCount: 2,
        color: null,
      },
      {
        id: "2",
        name: "Project 2",
        status: "ACTIVE",
        lastActivity: new Date().toISOString(),
        taskCount: 3,
        eventCount: 1,
        color: null,
      },
    ];

    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("active-projects")) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockProjects),
        } as Response);
      }
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText("(2)")).toBeInTheDocument(); // Project count badge
    });
  });

  it("should display error state when projects API fails", async (): Promise<void> => {
    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("active-projects")) {
        return Promise.resolve({
          ok: false,
          status: 500,
          statusText: "Internal Server Error",
          json: () =>
            Promise.resolve({ code: "SERVER_ERROR", message: "Failed to fetch projects" }),
        } as Response);
      }
      // Agent chains succeeds
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText(/unable to load projects/i)).toBeInTheDocument();
    });
  });

  it("should display error state when agent chains API fails", async (): Promise<void> => {
    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      if (urlString.includes("agent-chains")) {
        return Promise.resolve({
          ok: false,
          status: 500,
          statusText: "Internal Server Error",
          json: () => Promise.resolve({ code: "SERVER_ERROR", message: "Failed to fetch agents" }),
        } as Response);
      }
      // Active projects succeeds
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve([]),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText(/unable to load agents/i)).toBeInTheDocument();
    });
  });

  it("should display error state when both APIs fail", async (): Promise<void> => {
    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      // Both endpoints fail
      return Promise.resolve({
        ok: false,
        status: 500,
        statusText: "Internal Server Error",
        json: () => Promise.resolve({ code: "SERVER_ERROR", message: "Server error" }),
      } as Response);
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText(/unable to load projects/i)).toBeInTheDocument();
      expect(screen.getByText(/unable to load agents/i)).toBeInTheDocument();
    });
  });

  it("should handle network errors gracefully", async (): Promise<void> => {
    vi.mocked(global.fetch).mockImplementation((url: RequestInfo | URL) => {
      const urlString = typeof url === "string" ? url : url instanceof URL ? url.toString() : "";

      // Return CSRF token
      if (urlString.includes("csrf")) {
        return Promise.resolve(mockCsrfResponse());
      }
      // Network error
      return Promise.reject(new Error("Network error"));
    });

    render(<ActiveProjectsWidget id="active-projects-1" />);

    await waitFor(() => {
      expect(screen.getByText(/unable to load projects/i)).toBeInTheDocument();
      expect(screen.getByText(/unable to load agents/i)).toBeInTheDocument();
    });
  });
});
