package engine

import (
	"math"
	"testing"
)

func TestNewGrid(t *testing.T) {
	tests := []struct {
		name         string
		iconSize     float64
		paddingRatio float64
		wantPadding  int
		wantCell     int
	}{
		{
			name:         "standard 64px icon with 8% padding",
			iconSize:     64.0,
			paddingRatio: 0.08,
			wantPadding:  5, // 0.5 + 64 * 0.08 = 5.62, rounded to 5
			wantCell:     13, // (64 - 5*2) / 4 = 54/4 = 13.5, truncated to 13
		},
		{
			name:         "large 256px icon with 10% padding",
			iconSize:     256.0,
			paddingRatio: 0.10,
			wantPadding:  26, // 0.5 + 256 * 0.10 = 26.1, rounded to 26
			wantCell:     51, // (256 - 26*2) / 4 = 204/4 = 51
		},
		{
			name:         "small 32px icon with 5% padding",
			iconSize:     32.0,
			paddingRatio: 0.05,
			wantPadding:  2, // 0.5 + 32 * 0.05 = 2.1, rounded to 2
			wantCell:     7, // (32 - 2*2) / 4 = 28/4 = 7
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			grid := NewGrid(tt.iconSize, tt.paddingRatio)
			
			if grid.Padding != tt.wantPadding {
				t.Errorf("NewGrid() padding = %v, want %v", grid.Padding, tt.wantPadding)
			}
			
			if grid.Cell != tt.wantCell {
				t.Errorf("NewGrid() cell = %v, want %v", grid.Cell, tt.wantCell)
			}
			
			// Verify that the grid is centered
			expectedSize := tt.iconSize - float64(tt.wantPadding*2)
			if math.Abs(grid.Size-expectedSize) > 0.1 {
				t.Errorf("NewGrid() size = %v, want %v", grid.Size, expectedSize)
			}
		})
	}
}

func TestGridCellToCoordinate(t *testing.T) {
	grid := NewGrid(64.0, 0.08)
	
	tests := []struct {
		name  string
		cellX int
		cellY int
		wantX float64
		wantY float64
	}{
		{
			name:  "origin cell (0,0)",
			cellX: 0,
			cellY: 0,
			wantX: float64(grid.X),
			wantY: float64(grid.Y),
		},
		{
			name:  "center cell (1,1)",
			cellX: 1,
			cellY: 1,
			wantX: float64(grid.X + grid.Cell),
			wantY: float64(grid.Y + grid.Cell),
		},
		{
			name:  "corner cell (3,3)",
			cellX: 3,
			cellY: 3,
			wantX: float64(grid.X + 3*grid.Cell),
			wantY: float64(grid.Y + 3*grid.Cell),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotX, gotY := grid.CellToCoordinate(tt.cellX, tt.cellY)
			
			if gotX != tt.wantX {
				t.Errorf("CellToCoordinate() x = %v, want %v", gotX, tt.wantX)
			}
			
			if gotY != tt.wantY {
				t.Errorf("CellToCoordinate() y = %v, want %v", gotY, tt.wantY)
			}
		})
	}
}

func TestLayoutEngineGetShapePositions(t *testing.T) {
	le := NewLayoutEngine(64.0, 0.08)
	
	tests := []struct {
		name      string
		shapeType string
		wantLen   int
		wantFirst Position
		wantLast  Position
	}{
		{
			name:      "sides positions",
			shapeType: "sides",
			wantLen:   8,
			wantFirst: Position{1, 0},
			wantLast:  Position{0, 2},
		},
		{
			name:      "corners positions",
			shapeType: "corners",
			wantLen:   4,
			wantFirst: Position{0, 0},
			wantLast:  Position{0, 3},
		},
		{
			name:      "center positions",
			shapeType: "center",
			wantLen:   4,
			wantFirst: Position{1, 1},
			wantLast:  Position{1, 2},
		},
		{
			name:      "invalid shape type",
			shapeType: "invalid",
			wantLen:   0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			positions := le.GetShapePositions(tt.shapeType)
			
			if len(positions) != tt.wantLen {
				t.Errorf("GetShapePositions() len = %v, want %v", len(positions), tt.wantLen)
			}
			
			if tt.wantLen > 0 {
				if positions[0] != tt.wantFirst {
					t.Errorf("GetShapePositions() first = %v, want %v", positions[0], tt.wantFirst)
				}
				
				if positions[len(positions)-1] != tt.wantLast {
					t.Errorf("GetShapePositions() last = %v, want %v", positions[len(positions)-1], tt.wantLast)
				}
			}
		})
	}
}

func TestLayoutEngineGetTransformedPosition(t *testing.T) {
	le := NewLayoutEngine(64.0, 0.08)
	
	tests := []struct {
		name     string
		cellX    int
		cellY    int
		rotation int
		wantX    int // Expected cell X after rotation
		wantY    int // Expected cell Y after rotation
	}{
		{
			name:     "no rotation",
			cellX:    1,
			cellY:    0,
			rotation: 0,
			wantX:    1,
			wantY:    0,
		},
		{
			name:     "90 degree rotation",
			cellX:    1,
			cellY:    0,
			rotation: 1,
			wantX:    0,
			wantY:    2, // 3-1 = 2
		},
		{
			name:     "180 degree rotation",
			cellX:    1,
			cellY:    0,
			rotation: 2,
			wantX:    2, // 3-1 = 2
			wantY:    3, // 3-0 = 3
		},
		{
			name:     "270 degree rotation",
			cellX:    1,
			cellY:    0,
			rotation: 3,
			wantX:    3, // 3-0 = 3
			wantY:    1,
		},
		{
			name:     "rotation overflow (4 = 0)",
			cellX:    1,
			cellY:    0,
			rotation: 4,
			wantX:    1,
			wantY:    0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotX, gotY, gotCellSize := le.GetTransformedPosition(tt.cellX, tt.cellY, tt.rotation)
			
			// Convert back to cell coordinates to verify rotation
			expectedX, expectedY := le.grid.CellToCoordinate(tt.wantX, tt.wantY)
			
			if gotX != expectedX {
				t.Errorf("GetTransformedPosition() x = %v, want %v", gotX, expectedX)
			}
			
			if gotY != expectedY {
				t.Errorf("GetTransformedPosition() y = %v, want %v", gotY, expectedY)
			}
			
			if gotCellSize != float64(le.grid.Cell) {
				t.Errorf("GetTransformedPosition() cellSize = %v, want %v", gotCellSize, float64(le.grid.Cell))
			}
		})
	}
}

func TestApplySymmetry(t *testing.T) {
	positions := []Position{{0, 0}, {1, 0}, {2, 0}, {3, 0}}
	
	tests := []struct {
		name  string
		index int
		want  int // expected length
	}{
		{
			name:  "valid index",
			index: 1,
			want:  4,
		},
		{
			name:  "index out of bounds",
			index: 10,
			want:  4,
		},
		{
			name:  "negative index",
			index: -1,
			want:  4,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := ApplySymmetry(positions, tt.index)
			
			if len(result) != tt.want {
				t.Errorf("ApplySymmetry() len = %v, want %v", len(result), tt.want)
			}
			
			// Verify that the positions are unchanged (current implementation)
			for i, pos := range result {
				if pos != positions[i] {
					t.Errorf("ApplySymmetry() changed position at index %d: got %v, want %v", i, pos, positions[i])
				}
			}
		})
	}
}

func TestGridValidateGrid(t *testing.T) {
	tests := []struct {
		name string
		grid *Grid
		want bool
	}{
		{
			name: "valid grid",
			grid: &Grid{Size: 64, Cell: 16, Padding: 4},
			want: true,
		},
		{
			name: "zero cell size",
			grid: &Grid{Size: 64, Cell: 0, Padding: 4},
			want: false,
		},
		{
			name: "zero size",
			grid: &Grid{Size: 0, Cell: 16, Padding: 4},
			want: false,
		},
		{
			name: "negative padding",
			grid: &Grid{Size: 64, Cell: 16, Padding: -1},
			want: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.grid.ValidateGrid(); got != tt.want {
				t.Errorf("ValidateGrid() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGridGetIconBounds(t *testing.T) {
	grid := NewGrid(64.0, 0.08)
	
	x, y, width, height := grid.GetIconBounds()
	
	expectedX := float64(grid.X)
	expectedY := float64(grid.Y)
	expectedWidth := float64(grid.Cell * 4)
	expectedHeight := float64(grid.Cell * 4)
	
	if x != expectedX {
		t.Errorf("GetIconBounds() x = %v, want %v", x, expectedX)
	}
	
	if y != expectedY {
		t.Errorf("GetIconBounds() y = %v, want %v", y, expectedY)
	}
	
	if width != expectedWidth {
		t.Errorf("GetIconBounds() width = %v, want %v", width, expectedWidth)
	}
	
	if height != expectedHeight {
		t.Errorf("GetIconBounds() height = %v, want %v", height, expectedHeight)
	}
}

func TestGridGetCenterOffset(t *testing.T) {
	grid := NewGrid(64.0, 0.08)
	
	dx, dy := grid.GetCenterOffset()
	
	expected := float64(grid.Cell) / 2
	
	if dx != expected {
		t.Errorf("GetCenterOffset() dx = %v, want %v", dx, expected)
	}
	
	if dy != expected {
		t.Errorf("GetCenterOffset() dy = %v, want %v", dy, expected)
	}
}

func TestNewLayoutEngine(t *testing.T) {
	le := NewLayoutEngine(64.0, 0.08)
	
	if le.grid == nil {
		t.Error("NewLayoutEngine() grid is nil")
	}
	
	if le.Grid() != le.grid {
		t.Error("NewLayoutEngine() Grid() does not return internal grid")
	}
	
	// Verify grid configuration
	if !le.grid.ValidateGrid() {
		t.Error("NewLayoutEngine() created invalid grid")
	}
}