import { describe, it, expect, vi } from "vitest";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { GanttChart } from "./GanttChart";
import { TaskStatus, TaskPriority } from "@mosaic/shared";
import type { GanttTask } from "./types";

describe("GanttChart", () => {
  const baseDate = new Date("2026-02-01T00:00:00Z");
  
  const createGanttTask = (overrides: Partial<GanttTask> = {}): GanttTask => ({
    id: `task-${Math.random()}`,
    workspaceId: "workspace-1",
    title: "Sample Task",
    description: null,
    status: TaskStatus.NOT_STARTED,
    priority: TaskPriority.MEDIUM,
    dueDate: new Date("2026-02-15T00:00:00Z"),
    assigneeId: null,
    creatorId: "user-1",
    projectId: null,
    parentId: null,
    sortOrder: 0,
    metadata: {},
    completedAt: null,
    createdAt: baseDate,
    updatedAt: baseDate,
    startDate: new Date("2026-02-01T00:00:00Z"),
    endDate: new Date("2026-02-15T00:00:00Z"),
    ...overrides,
  });

  describe("Rendering", () => {
    it("should render without crashing with empty task list", () => {
      render(<GanttChart tasks={[]} />);
      expect(screen.getByRole("region", { name: /gantt chart/i })).toBeInTheDocument();
    });

    it("should render task names in the task list", () => {
      const tasks = [
        createGanttTask({ id: "task-1", title: "Design mockups" }),
        createGanttTask({ id: "task-2", title: "Implement frontend" }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      // Tasks appear in both the list and bars, so use getAllByText
      expect(screen.getAllByText("Design mockups").length).toBeGreaterThan(0);
      expect(screen.getAllByText("Implement frontend").length).toBeGreaterThan(0);
    });

    it("should render timeline bars for each task", () => {
      const tasks = [
        createGanttTask({ id: "task-1", title: "Task 1" }),
        createGanttTask({ id: "task-2", title: "Task 2" }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      const bars = screen.getAllByRole("button", { name: /gantt bar/i });
      expect(bars).toHaveLength(2);
    });

    it("should display date headers for the timeline", () => {
      const tasks = [
        createGanttTask({
          startDate: new Date("2026-02-01"),
          endDate: new Date("2026-02-10"),
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      // Should show month or date indicators
      const timeline = screen.getByRole("region", { name: /timeline/i });
      expect(timeline).toBeInTheDocument();
    });
  });

  describe("Task Status Indicators", () => {
    it("should visually distinguish completed tasks", () => {
      const tasks = [
        createGanttTask({
          id: "completed-task",
          title: "Completed Task",
          status: TaskStatus.COMPLETED,
          completedAt: new Date("2026-02-10"),
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      const taskRow = screen.getAllByText("Completed Task")[0].closest("[role='row']");
      expect(taskRow?.className).toMatch(/Completed/i);
    });

    it("should visually distinguish in-progress tasks", () => {
      const tasks = [
        createGanttTask({
          id: "active-task",
          title: "Active Task",
          status: TaskStatus.IN_PROGRESS,
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      const taskRow = screen.getAllByText("Active Task")[0].closest("[role='row']");
      expect(taskRow?.className).toMatch(/InProgress/i);
    });
  });

  describe("PDA-friendly language", () => {
    it('should show "Target passed" for tasks past their end date', () => {
      const pastTask = createGanttTask({
        id: "past-task",
        title: "Past Task",
        startDate: new Date("2020-01-01"),
        endDate: new Date("2020-01-15"),
        status: TaskStatus.NOT_STARTED,
      });
      
      render(<GanttChart tasks={[pastTask]} />);
      
      // Should show "Target passed" not "OVERDUE"
      expect(screen.getByText(/target passed/i)).toBeInTheDocument();
      expect(screen.queryByText(/overdue/i)).not.toBeInTheDocument();
    });

    it('should show "Approaching target" for tasks near their end date', () => {
      const today = new Date();
      const tomorrow = new Date(today);
      tomorrow.setDate(tomorrow.getDate() + 1);
      
      const soonTask = createGanttTask({
        id: "soon-task",
        title: "Soon Task",
        startDate: today,
        endDate: tomorrow,
        status: TaskStatus.IN_PROGRESS,
      });
      
      render(<GanttChart tasks={[soonTask]} />);
      
      expect(screen.getByText(/approaching target/i)).toBeInTheDocument();
    });
  });

  describe("Task Interactions", () => {
    it("should call onTaskClick when a task bar is clicked", async () => {
      const user = userEvent.setup();
      const onTaskClick = vi.fn();
      
      const task = createGanttTask({ id: "clickable-task", title: "Click Me" });
      
      render(<GanttChart tasks={[task]} onTaskClick={onTaskClick} />);
      
      const taskBar = screen.getByRole("button", { name: /gantt bar.*click me/i });
      await user.click(taskBar);
      
      expect(onTaskClick).toHaveBeenCalledWith(task);
    });

    it("should not crash when clicking a task without onTaskClick handler", async () => {
      const user = userEvent.setup();
      const task = createGanttTask({ id: "task-1", title: "No Handler" });
      
      render(<GanttChart tasks={[task]} />);
      
      const taskBar = screen.getByRole("button", { name: /gantt bar/i });
      await user.click(taskBar);
      
      // Should not throw
      expect(taskBar).toBeInTheDocument();
    });
  });

  describe("Timeline Calculations", () => {
    it("should calculate timeline range from task dates", () => {
      const tasks = [
        createGanttTask({
          id: "early-task",
          startDate: new Date("2026-01-01"),
          endDate: new Date("2026-01-10"),
        }),
        createGanttTask({
          id: "late-task",
          startDate: new Date("2026-03-01"),
          endDate: new Date("2026-03-31"),
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      // Timeline should span from earliest start to latest end
      const timeline = screen.getByRole("region", { name: /timeline/i });
      expect(timeline).toBeInTheDocument();
    });

    it("should position task bars proportionally to their dates", () => {
      const tasks = [
        createGanttTask({
          id: "task-1",
          title: "Task 1",
          startDate: new Date("2026-02-01"),
          endDate: new Date("2026-02-05"), // 4 days
        }),
        createGanttTask({
          id: "task-2",
          title: "Task 2",
          startDate: new Date("2026-02-01"),
          endDate: new Date("2026-02-11"), // 10 days
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      const bars = screen.getAllByRole("button", { name: /gantt bar/i });
      expect(bars).toHaveLength(2);
      
      // Second bar should be wider (more days)
      const bar1Width = bars[0].style.width;
      const bar2Width = bars[1].style.width;
      
      // Basic check that widths are set (exact values depend on implementation)
      expect(bar1Width).toBeTruthy();
      expect(bar2Width).toBeTruthy();
    });
  });

  describe("Accessibility", () => {
    it("should have proper ARIA labels for the chart region", () => {
      render(<GanttChart tasks={[]} />);
      
      expect(screen.getByRole("region", { name: /gantt chart/i })).toBeInTheDocument();
    });

    it("should have proper ARIA labels for task bars", () => {
      const task = createGanttTask({
        id: "task-1",
        title: "Accessible Task",
        startDate: new Date("2026-02-01"),
        endDate: new Date("2026-02-15"),
      });
      
      render(<GanttChart tasks={[task]} />);
      
      const taskBar = screen.getByRole("button", {
        name: /gantt bar.*accessible task/i,
      });
      expect(taskBar).toHaveAccessibleName();
    });

    it("should be keyboard navigable", async () => {
      const user = userEvent.setup();
      const onTaskClick = vi.fn();
      
      const task = createGanttTask({ id: "task-1", title: "Keyboard Task" });
      
      render(<GanttChart tasks={[task]} onTaskClick={onTaskClick} />);
      
      const taskBar = screen.getByRole("button", { name: /gantt bar/i });
      
      // Tab to focus
      await user.tab();
      expect(taskBar).toHaveFocus();
      
      // Enter to activate
      await user.keyboard("{Enter}");
      expect(onTaskClick).toHaveBeenCalled();
    });
  });

  describe("Responsive Design", () => {
    it("should accept custom height prop", () => {
      const tasks = [createGanttTask({ id: "task-1" })];
      
      render(<GanttChart tasks={tasks} height={600} />);
      
      const chart = screen.getByRole("region", { name: /gantt chart/i });
      expect(chart).toHaveStyle({ height: "600px" });
    });

    it("should use default height when not specified", () => {
      const tasks = [createGanttTask({ id: "task-1" })];
      
      render(<GanttChart tasks={tasks} />);
      
      const chart = screen.getByRole("region", { name: /gantt chart/i });
      expect(chart).toBeInTheDocument();
      // Default height should be set in implementation
    });
  });

  describe("Edge Cases", () => {
    it("should handle tasks with same start and end date", () => {
      const sameDay = new Date("2026-02-01");
      const task = createGanttTask({
        id: "same-day",
        title: "Same Day Task",
        startDate: sameDay,
        endDate: sameDay,
      });
      
      render(<GanttChart tasks={[task]} />);
      
      expect(screen.getAllByText("Same Day Task").length).toBeGreaterThan(0);
      const bar = screen.getByRole("button", { name: /gantt bar/i });
      expect(bar).toBeInTheDocument();
      // Bar should have minimum width
    });

    it("should handle tasks with very long duration", () => {
      const task = createGanttTask({
        id: "long-task",
        title: "Long Task",
        startDate: new Date("2026-01-01"),
        endDate: new Date("2027-12-31"), // 2 years
      });
      
      render(<GanttChart tasks={[task]} />);
      
      expect(screen.getAllByText("Long Task").length).toBeGreaterThan(0);
    });

    it("should sort tasks by start date", () => {
      const tasks = [
        createGanttTask({
          id: "late-task",
          title: "Late Task",
          startDate: new Date("2026-03-01"),
        }),
        createGanttTask({
          id: "early-task",
          title: "Early Task",
          startDate: new Date("2026-01-01"),
        }),
        createGanttTask({
          id: "mid-task",
          title: "Mid Task",
          startDate: new Date("2026-02-01"),
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      const taskNames = screen.getAllByRole("row").map((row) => row.textContent);
      
      // Early Task should appear first
      const earlyIndex = taskNames.findIndex((name) => name?.includes("Early Task"));
      const lateIndex = taskNames.findIndex((name) => name?.includes("Late Task"));
      
      expect(earlyIndex).toBeLessThan(lateIndex);
    });
  });

  describe("Dependencies", () => {
    it("should render dependency lines when showDependencies is true", () => {
      const tasks = [
        createGanttTask({
          id: "task-1",
          title: "Foundation",
          startDate: new Date("2026-02-01"),
          endDate: new Date("2026-02-10"),
        }),
        createGanttTask({
          id: "task-2",
          title: "Build on top",
          startDate: new Date("2026-02-11"),
          endDate: new Date("2026-02-20"),
          dependencies: ["task-1"],
        }),
      ];
      
      render(<GanttChart tasks={tasks} showDependencies={true} />);
      
      // Check if dependency SVG exists
      const chart = screen.getByRole("region", { name: /gantt chart/i });
      expect(chart).toBeInTheDocument();
      
      // Look for dependency path element
      const svg = chart.querySelector(".gantt-dependencies");
      expect(svg).toBeInTheDocument();
      
      const paths = chart.querySelectorAll(".dependency-line");
      expect(paths).toHaveLength(1);
    });

    it("should not render dependencies by default", () => {
      const tasks = [
        createGanttTask({
          id: "task-1",
          title: "Task 1",
        }),
        createGanttTask({
          id: "task-2",
          title: "Task 2",
          dependencies: ["task-1"],
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      // Dependencies should not be shown by default
      const chart = screen.getByRole("region", { name: /gantt chart/i });
      expect(chart).toBeInTheDocument();
      
      const svg = chart.querySelector(".gantt-dependencies");
      expect(svg).not.toBeInTheDocument();
    });

    it("should handle tasks with non-existent dependencies gracefully", () => {
      const tasks = [
        createGanttTask({
          id: "task-1",
          title: "Task 1",
          dependencies: ["non-existent-task"],
        }),
      ];
      
      render(<GanttChart tasks={tasks} showDependencies={true} />);
      
      // Should not crash
      const chart = screen.getByRole("region", { name: /gantt chart/i });
      expect(chart).toBeInTheDocument();
    });

    it("should render multiple dependency lines", () => {
      const tasks = [
        createGanttTask({
          id: "task-1",
          title: "Task 1",
          startDate: new Date("2026-02-01"),
          endDate: new Date("2026-02-05"),
        }),
        createGanttTask({
          id: "task-2",
          title: "Task 2",
          startDate: new Date("2026-02-01"),
          endDate: new Date("2026-02-05"),
        }),
        createGanttTask({
          id: "task-3",
          title: "Task 3",
          startDate: new Date("2026-02-06"),
          endDate: new Date("2026-02-10"),
          dependencies: ["task-1", "task-2"],
        }),
      ];
      
      render(<GanttChart tasks={tasks} showDependencies={true} />);
      
      const chart = screen.getByRole("region", { name: /gantt chart/i });
      const paths = chart.querySelectorAll(".dependency-line");
      expect(paths).toHaveLength(2);
    });
  });

  describe("Milestones", () => {
    it("should render milestone as diamond shape", () => {
      const milestone = createGanttTask({
        id: "milestone-1",
        title: "Phase 1 Complete",
        startDate: new Date("2026-02-15"),
        endDate: new Date("2026-02-15"),
        isMilestone: true,
      });
      
      render(<GanttChart tasks={[milestone]} />);
      
      const milestoneElement = screen.getByRole("button", {
        name: /milestone.*phase 1 complete/i,
      });
      expect(milestoneElement).toBeInTheDocument();
      expect(milestoneElement).toHaveClass("gantt-milestone");
    });

    it("should handle click on milestone", async () => {
      const user = userEvent.setup();
      const onTaskClick = vi.fn();
      
      const milestone = createGanttTask({
        id: "milestone-1",
        title: "Milestone Task",
        isMilestone: true,
      });
      
      render(<GanttChart tasks={[milestone]} onTaskClick={onTaskClick} />);
      
      const milestoneElement = screen.getByRole("button", {
        name: /milestone.*milestone task/i,
      });
      await user.click(milestoneElement);
      
      expect(onTaskClick).toHaveBeenCalledWith(milestone);
    });

    it("should support keyboard navigation for milestones", async () => {
      const user = userEvent.setup();
      const onTaskClick = vi.fn();
      
      const milestone = createGanttTask({
        id: "milestone-1",
        title: "Keyboard Milestone",
        isMilestone: true,
      });
      
      render(<GanttChart tasks={[milestone]} onTaskClick={onTaskClick} />);
      
      const milestoneElement = screen.getByRole("button", {
        name: /milestone/i,
      });
      
      await user.tab();
      expect(milestoneElement).toHaveFocus();
      
      await user.keyboard("{Enter}");
      expect(onTaskClick).toHaveBeenCalled();
    });

    it("should render milestones and regular tasks together", () => {
      const tasks = [
        createGanttTask({
          id: "task-1",
          title: "Regular Task",
          isMilestone: false,
        }),
        createGanttTask({
          id: "milestone-1",
          title: "Milestone",
          isMilestone: true,
        }),
      ];
      
      render(<GanttChart tasks={tasks} />);
      
      expect(screen.getByRole("button", { name: /gantt bar.*regular task/i })).toBeInTheDocument();
      expect(screen.getByRole("button", { name: /milestone.*milestone/i })).toBeInTheDocument();
    });
  });
});
