import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { LineItemsEditor } from "@/components/po/po-line-items-editor";
import type { LineItemInput } from "@/lib/validations/po";

const DEFAULT_ITEM: LineItemInput = {
  name: "Bearing Assembly",
  description: "Test Item",
  quantity: 10,
  unit: "pc",
  unitPrice: 100,
  gstRate: 0.18,
};

// ── Render (edit mode) ────────────────────────────────────────────────────────

describe("LineItemsEditor — edit mode", () => {
  it("renders one row by default when one item provided", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    // Each row has a description input
    expect(screen.getAllByPlaceholderText("Description (optional)")).toHaveLength(1);
  });

  it("shows the initial description value", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    const input = screen.getByPlaceholderText("Description (optional)") as HTMLInputElement;
    expect(input.value).toBe("Test Item");
  });

  it("shows the initial quantity value", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    const inputs = screen.getAllByRole("spinbutton");
    expect(inputs[0].getAttribute("value") ?? (inputs[0] as HTMLInputElement).value).toBe("10");
  });

  it("shows 18% as the default GST rate", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    const selects = screen.getAllByRole("combobox") as HTMLSelectElement[];
    const gstSelect = selects.find((s) => s.value === "0.18");
    expect(gstSelect).toBeTruthy();
    expect(gstSelect!.value).toBe("0.18");
  });

  it("disables the remove button when only one row exists", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    const removeBtn = screen.getByRole("button", { name: /delete|remove|trash/i });
    expect(removeBtn).toBeDisabled();
  });

  it("calls onChange when description is changed", async () => {
    const onChange = vi.fn();
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={onChange} />);
    const input = screen.getByPlaceholderText("Description (optional)");
    await userEvent.clear(input);
    await userEvent.type(input, "Gear Oil");
    expect(onChange).toHaveBeenCalled();
    const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1][0] as LineItemInput[];
    expect(lastCall[0].description).toBe("Gear Oil");
  });

  it("adds a row when 'Add line item' is clicked", async () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    const addBtn = screen.getByRole("button", { name: /add line item/i });
    await userEvent.click(addBtn);
    expect(screen.getAllByPlaceholderText("Description (optional)")).toHaveLength(2);
  });

  it("enables remove button after a second row is added", async () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    await userEvent.click(screen.getByRole("button", { name: /add line item/i }));
    const removeBtns = screen.getAllByRole("button", { name: /delete|remove|trash/i });
    removeBtns.forEach((btn) => expect(btn).not.toBeDisabled());
  });

  it("removes a row when delete is clicked (with 2 rows)", async () => {
    const items: LineItemInput[] = [
      { ...DEFAULT_ITEM, description: "Item A" },
      { ...DEFAULT_ITEM, description: "Item B" },
    ];
    render(<LineItemsEditor items={items} onChange={vi.fn()} />);
    const removeBtns = screen.getAllByRole("button", { name: /delete|remove|trash/i });
    await userEvent.click(removeBtns[0]);
    expect(screen.getAllByPlaceholderText("Description (optional)")).toHaveLength(1);
  });

  it("calls onChange with updated gstRate when GST dropdown changes", async () => {
    const onChange = vi.fn();
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={onChange} />);
    const selects = screen.getAllByRole("combobox") as HTMLSelectElement[];
    const gstSelect = selects.find((s) => s.value === "0.18")!;
    fireEvent.change(gstSelect, { target: { value: "0.05" } });
    const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1][0] as LineItemInput[];
    expect(lastCall[0].gstRate).toBeCloseTo(0.05);
  });

  it("offers month and year as unit-of-measure options", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    const selects = screen.getAllByRole("combobox") as HTMLSelectElement[];
    const unitSelect = selects.find((s) => s.value === "pc")!;
    const values = Array.from(unitSelect.options).map((o) => o.value);
    expect(values).toContain("month");
    expect(values).toContain("year");
  });

  it("calls onChange with the selected duration unit", async () => {
    const onChange = vi.fn();
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={onChange} />);
    const selects = screen.getAllByRole("combobox") as HTMLSelectElement[];
    const unitSelect = selects.find((s) => s.value === "pc")!;
    fireEvent.change(unitSelect, { target: { value: "year" } });
    const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1][0] as LineItemInput[];
    expect(lastCall[0].unit).toBe("year");
  });
});

// ── Totals calculation (edit mode) ────────────────────────────────────────────

describe("LineItemsEditor — totals calculation", () => {
  it("shows correct taxable subtotal (qty × unit price)", () => {
    // qty=10, unitPrice=100 → taxable=1000
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    expect(screen.getByText(/Taxable subtotal/i)).toBeInTheDocument();
    // Should show 1,000 somewhere in the footer
    const text = document.body.textContent ?? "";
    expect(text).toMatch(/1[,.]?000/);
  });

  it("shows correct GST amount (taxable × gstRate)", () => {
    // 1000 × 0.18 = 180
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    expect(screen.getByText(/^GST$/i)).toBeInTheDocument();
    const text = document.body.textContent ?? "";
    expect(text).toMatch(/180/);
  });

  it("shows correct grand total (taxable + GST)", () => {
    // 1000 + 180 = 1180
    render(<LineItemsEditor items={[DEFAULT_ITEM]} onChange={vi.fn()} />);
    expect(screen.getByText(/Grand Total/i)).toBeInTheDocument();
    const text = document.body.textContent ?? "";
    expect(text).toMatch(/1[,.]?180/);
  });

  it("sums multiple line items correctly", () => {
    const items: LineItemInput[] = [
      { name: "Item A", description: "Item A", quantity: 5,  unit: "pc", unitPrice: 100, gstRate: 0.18 },
      { name: "Item B", description: "Item B", quantity: 10, unit: "L",  unitPrice: 50,  gstRate: 0.18 },
    ];
    // Taxable: 500 + 500 = 1000; GST: 180; Grand: 1180
    render(<LineItemsEditor items={items} onChange={vi.fn()} />);
    const text = document.body.textContent ?? "";
    expect(text).toMatch(/1[,.]?000/); // taxable
    expect(text).toMatch(/1[,.]?180/); // grand total
  });

  it("shows correct per-row total when GST rate is 0% (zero-rated)", () => {
    // qty=1, unitPrice=1000, gstRate=0 → row total = ₹1,000.00, NOT ₹1,180.00
    const item: LineItemInput = { name: "Test Item", quantity: 1, unit: "pc", unitPrice: 1000, gstRate: 0 };
    render(<LineItemsEditor items={[item]} onChange={vi.fn()} />);
    const text = document.body.textContent ?? "";
    // Grand total should be 1000, not 1180
    expect(text).toMatch(/1[,.]?000/);
    expect(text).not.toMatch(/1[,.]?180/);
  });

  it("shows correct per-row total when GST rate is 0% after changing from 18%", async () => {
    // Start with 18%, change to 0%, verify row total updates correctly
    const item: LineItemInput = { name: "Test Item", quantity: 1, unit: "pc", unitPrice: 1000, gstRate: 0.18 };
    render(<LineItemsEditor items={[item]} onChange={vi.fn()} />);
    const selects = screen.getAllByRole("combobox") as HTMLSelectElement[];
    const gstSelect = selects.find((s) => s.value === "0.18")!;
    fireEvent.change(gstSelect, { target: { value: "0" } });
    // After changing to 0%, total should be 1000 not 1180
    const text = document.body.textContent ?? "";
    expect(text).not.toMatch(/1[,.]?180/);
    expect(text).toMatch(/1[,.]?000/);
  });
});

// ── Read-only mode ────────────────────────────────────────────────────────────

describe("LineItemsEditor — read-only mode", () => {
  it("renders without input fields", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} readOnly />);
    expect(screen.queryByPlaceholderText("Item description")).not.toBeInTheDocument();
    expect(screen.queryByRole("button", { name: /add line item/i })).not.toBeInTheDocument();
  });

  it("displays the description as text", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} readOnly />);
    expect(screen.getByText("Test Item")).toBeInTheDocument();
  });

  it("shows GST% column in read-only mode", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} readOnly />);
    expect(screen.getByText("18%")).toBeInTheDocument();
  });

  it("shows taxable, GST, grand total rows in read-only footer", () => {
    render(<LineItemsEditor items={[DEFAULT_ITEM]} readOnly />);
    expect(screen.getByText(/Taxable subtotal/i)).toBeInTheDocument();
    expect(screen.getByText(/^GST$/i)).toBeInTheDocument();
    expect(screen.getByText(/Grand Total/i)).toBeInTheDocument();
  });

  it("shows manager-amended diff banner when originalItems provided", () => {
    const original: LineItemInput[] = [{ ...DEFAULT_ITEM, unitPrice: 80 }];
    render(<LineItemsEditor items={[DEFAULT_ITEM]} readOnly originalItems={original} />);
    expect(screen.getByText(/amended by manager/i)).toBeInTheDocument();
  });

  it("shows strikethrough on changed unit price when diff available", () => {
    const original: LineItemInput[] = [{ ...DEFAULT_ITEM, unitPrice: 80 }];
    render(<LineItemsEditor items={[DEFAULT_ITEM]} readOnly originalItems={original} />);
    // The original price 80 should appear with line-through styling
    const text = document.body.textContent ?? "";
    expect(text).toMatch(/80/);
    expect(text).toMatch(/100/);
  });
});
