package table

import (
	"testing"

	pdf "ragflow/internal/deepdoc/parser/pdf/type"
)

func cellTexts(cells []pdf.TSRCell) []string {
	out := make([]string, len(cells))
	for i, c := range cells {
		out[i] = c.Text
	}
	return out
}

func TestGroupTSRCellsToRows(t *testing.T) {
	t.Run("empty", func(t *testing.T) {
		if rows := GroupTSRCellsToRows(nil); rows != nil {
			t.Error("nil → nil")
		}
		if rows := GroupTSRCellsToRows([]pdf.TSRCell{}); rows != nil {
			t.Error("empty → nil")
		}
	})

	t.Run("single cell", func(t *testing.T) {
		cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 100, Y1: 50, Text: "A"}}
		rows := GroupTSRCellsToRows(cells)
		if len(rows) != 1 || rows[0][0].Text != "A" {
			t.Error("single cell not preserved")
		}
	})

	t.Run("two rows two cols", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "A"},
			{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
			{X0: 0, Y0: 50, X1: 50, Y1: 80, Text: "C"},
			{X0: 50, Y0: 50, X1: 100, Y1: 80, Text: "D"},
		}
		rows := GroupTSRCellsToRows(cells)
		if len(rows) != 2 {
			t.Fatalf("2 rows expected, got %d", len(rows))
		}
		if rows[0][0].Text != "A" || rows[0][1].Text != "B" {
			t.Errorf("row0: %v", cellTexts(rows[0]))
		}
		if rows[1][0].Text != "C" || rows[1][1].Text != "D" {
			t.Errorf("row1: %v", cellTexts(rows[1]))
		}
	})

	t.Run("unsorted input", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 50, Y0: 50, X1: 100, Y1: 80, Text: "D"},
			{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "A"},
			{X0: 0, Y0: 50, X1: 50, Y1: 80, Text: "C"},
			{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
		}
		rows := GroupTSRCellsToRows(cells)
		if len(rows) != 2 {
			t.Fatalf("unsorted: 2 rows expected, got %d", len(rows))
		}
		if rows[0][0].Text != "A" || rows[0][1].Text != "B" {
			t.Errorf("unsorted row0: %v", cellTexts(rows[0]))
		}
	})

	t.Run("tall merged cell", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 50, Y1: 100, Text: "merged"},
			{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
			{X0: 50, Y0: 50, X1: 100, Y1: 80, Text: "D"},
		}
		rows := GroupTSRCellsToRows(cells)
		// merged cell starts Y0=0 → row 0; Y0=50 cell → row 1
		if len(rows) != 2 {
			t.Fatalf("merged cell: 2 rows expected, got %d", len(rows))
		}
	})

	t.Run("large gap different rows", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "top"},
			{X0: 0, Y0: 200, X1: 50, Y1: 230, Text: "far"},
		}
		rows := GroupTSRCellsToRows(cells)
		if len(rows) != 2 {
			t.Fatalf("large gap: 2 rows expected, got %d", len(rows))
		}
	})

	t.Run("close rows", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 10, Y1: 8, Text: "Row1"},
			{X0: 0, Y0: 9, X1: 10, Y1: 17, Text: "Row2"},
		}
		rows := GroupTSRCellsToRows(cells)
		if len(rows) != 2 {
			t.Errorf("close rows: expected 2, got %d", len(rows))
		}
	})

	t.Run("varying heights", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 10, Y1: 5, Text: "A"},
			{X0: 0, Y0: 50, X1: 10, Y1: 70, Text: "B"},
			{X0: 0, Y0: 50, X1: 10, Y1: 70, Text: "C"},
		}
		rows := GroupTSRCellsToRows(cells)
		if len(rows) != 2 {
			t.Fatalf("varying heights: expected 2 rows, got %d", len(rows))
		}
		if len(rows[0]) != 1 || rows[0][0].Text != "A" {
			t.Errorf("row 0: expected [A], got %v", cellTexts(rows[0]))
		}
	})
}

// ── fillCellTextFromBoxes ──────────────────────────────────────────────

func TestFillCellTextFromBoxes(t *testing.T) {
	t.Run("exact match", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 100, Y1: 50},
			{X0: 100, Y0: 0, X1: 200, Y1: 50},
		}
		boxes := []pdf.TextBox{
			{X0: 0, X1: 100, Top: 0, Bottom: 50, Text: "A"},
			{X0: 100, X1: 200, Top: 0, Bottom: 50, Text: "B"},
		}
		FillCellTextFromBoxes(cells, boxes)
		if cells[0].Text != "A" || cells[1].Text != "B" {
			t.Errorf("got %q/%q, want A/B", cells[0].Text, cells[1].Text)
		}
	})

	t.Run("empty cells", func(t *testing.T) {
		cells := []pdf.TSRCell{
			{X0: 0, Y0: 0, X1: 100, Y1: 50},
			{X0: 100, Y0: 0, X1: 200, Y1: 50},
		}
		boxes := []pdf.TextBox{
			{X0: 0, X1: 100, Top: 0, Bottom: 50, Text: "only first"},
		}
		FillCellTextFromBoxes(cells, boxes)
		if cells[0].Text != "only first" {
			t.Errorf("cell[0]: got %q", cells[0].Text)
		}
		if cells[1].Text != "" {
			t.Errorf("cell[1] should be empty, got %q", cells[1].Text)
		}
	})

	t.Run("partial cell coverage — empty cell filled from any overlapping box", func(t *testing.T) {
		// Box covers 40% of cell area.  Old code rejected (<85% cell coverage).
		// New code: cell is empty → accepts box (≥30% box area inside cell).
		cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 200, Y1: 50}}
		boxes := []pdf.TextBox{{X0: 0, X1: 80, Top: 0, Bottom: 50, Text: "partial"}}
		FillCellTextFromBoxes(cells, boxes)
		if cells[0].Text != "partial" {
			t.Errorf("empty cell should be filled from overlapping box, got %q", cells[0].Text)
		}
	})

	t.Run("box inside cell >85%", func(t *testing.T) {
		cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 500, Y1: 300}}
		boxes := []pdf.TextBox{{X0: 10, X1: 490, Top: 10, Bottom: 290, Text: "inside"}}
		FillCellTextFromBoxes(cells, boxes)
		if cells[0].Text != "inside" {
			t.Errorf("got %q", cells[0].Text)
		}
	})

	t.Run("concatenate two boxes to same cell", func(t *testing.T) {
		cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 200, Y1: 100}}
		boxes := []pdf.TextBox{
			{X0: 5, X1: 195, Top: 2, Bottom: 98, Text: "hello"},
			{X0: 5, X1: 195, Top: 2, Bottom: 98, Text: "world"},
		}
		FillCellTextFromBoxes(cells, boxes)
		if cells[0].Text != "hello world" {
			t.Errorf("got %q, want 'hello world'", cells[0].Text)
		}
	})

	t.Run("empty inputs", func(t *testing.T) {
		FillCellTextFromBoxes(nil, nil)
		FillCellTextFromBoxes([]pdf.TSRCell{}, []pdf.TextBox{})
		c := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 1, Y1: 1}}
		FillCellTextFromBoxes(c, nil)
		if c[0].Text != "" {
			t.Error("no boxes → text empty")
		}
	})
}

// ── enrichOnePageWithDeepDoc noop ──────────────────────────────────────

func TestGroupTSRCellsToRows_SameHeight(t *testing.T) {
	// All cells have identical height → medianH is that value → threshold = medianH/2
	cells := []pdf.TSRCell{
		{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "A"},
		{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
		{X0: 0, Y0: 31, X1: 50, Y1: 61, Text: "C"}, // gap = 31-30=1 < 30/2=15 → same row? NO, Y0=31 is right at edge
	}
	rows := GroupTSRCellsToRows(cells)
	// medianH=30, threshold=15. C.Y0=31 > curY+threshold?" curY=0, 31 > 15 → new row.
	// So A,B in row 0, C in row 1.
	if len(rows) != 2 {
		t.Fatalf("expected 2 rows, got %d", len(rows))
	}
	if len(rows[0]) != 2 || len(rows[1]) != 1 {
		t.Errorf("row sizes: %d %d, want 2 1", len(rows[0]), len(rows[1]))
	}
}

func TestFillCellTextFromBoxes_WhitespaceTrim(t *testing.T) {
	cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 100, Y1: 100}}
	boxes := []pdf.TextBox{{X0: 0, X1: 100, Top: 0, Bottom: 100, Text: "  hello  "}}
	FillCellTextFromBoxes(cells, boxes)
	if cells[0].Text != "hello" {
		t.Errorf("got %q, want 'hello'", cells[0].Text)
	}
}

func TestFillCellTextFromBoxes_EmptyBoxIgnored(t *testing.T) {
	cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 100, Y1: 100}}
	boxes := []pdf.TextBox{{X0: 0, X1: 100, Top: 0, Bottom: 100, Text: "   "}} // all whitespace
	FillCellTextFromBoxes(cells, boxes)
	if cells[0].Text != "" {
		t.Errorf("whitespace text should produce empty, got %q", cells[0].Text)
	}
}
