// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Three Sum Problem Solution

package main

import (
	"reflect"
	"sort"
	"testing"
)

// Test cases for 3Sum problem
var threeSumTestCases = []struct {
	nums     []int
	expected [][]int
}{
	{
		nums:     []int{-1, 0, 1, 2, -1, -4},
		expected: [][]int{{-1, -1, 2}, {-1, 0, 1}},
	},
	{
		nums:     []int{0, 1, 1},
		expected: [][]int{},
	},
	{
		nums:     []int{0, 0, 0},
		expected: [][]int{{0, 0, 0}},
	},
	{
		nums:     []int{-2, 0, 1, 1, 2},
		expected: [][]int{{-2, 0, 2}, {-2, 1, 1}},
	},
	{
		nums:     []int{1, 2, -2, -1},
		expected: [][]int{},
	},
}

// Test cases for 3Sum with target
var threeSumTargetTestCases = []struct {
	nums     []int
	target   int
	expected [][]int
}{
	{
		nums:     []int{1, 2, 3, 4, 5, 6},
		target:   6,
		expected: [][]int{{1, 2, 3}},
	},
	{
		nums:     []int{-1, 0, 1, 2, -1, -4},
		target:   3,
		expected: [][]int{{-1, 2, 2}}, // Note: This would need duplicate 2s in input
	},
}

// Test cases for 3Sum closest
var threeSumClosestTestCases = []struct {
	nums     []int
	target   int
	expected int
}{
	{
		nums:     []int{-1, 2, 1, -4},
		target:   1,
		expected: 2, // -1 + 2 + 1 = 2
	},
	{
		nums:     []int{0, 0, 0},
		target:   1,
		expected: 0, // 0 + 0 + 0 = 0
	},
}

func TestThreeSumOptimal(t *testing.T) {
	for i, tc := range threeSumTestCases {
		result := threeSumOptimal(tc.nums)
		
		// Sort both result and expected for comparison
		sortTriplets(result)
		sortTriplets(tc.expected)
		
		if !reflect.DeepEqual(result, tc.expected) {
			t.Errorf("Test case %d: threeSumOptimal(%v) = %v; expected %v", 
				i, tc.nums, result, tc.expected)
		}
	}
}

func TestThreeSumBruteForce(t *testing.T) {
	for i, tc := range threeSumTestCases {
		result := threeSumBruteForce(tc.nums)
		
		// Sort both result and expected for comparison
		sortTriplets(result)
		sortTriplets(tc.expected)
		
		if !reflect.DeepEqual(result, tc.expected) {
			t.Errorf("Test case %d: threeSumBruteForce(%v) = %v; expected %v", 
				i, tc.nums, result, tc.expected)
		}
	}
}

func TestThreeSumTarget(t *testing.T) {
	// Test with simple case
	nums := []int{1, 2, 3, 4, 5, 6}
	target := 6
	result := threeSumTarget(nums, target)
	expected := [][]int{{1, 2, 3}}
	
	sortTriplets(result)
	sortTriplets(expected)
	
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("threeSumTarget(%v, %d) = %v; expected %v", 
			nums, target, result, expected)
	}
}

func TestThreeSumClosest(t *testing.T) {
	for i, tc := range threeSumClosestTestCases {
		result := threeSumClosest(tc.nums, tc.target)
		
		if result != tc.expected {
			t.Errorf("Test case %d: threeSumClosest(%v, %d) = %d; expected %d", 
				i, tc.nums, tc.target, result, tc.expected)
		}
	}
}

func TestParseIntArray(t *testing.T) {
	testCases := []struct {
		input    string
		expected []int
		hasError bool
	}{
		{"[1,2,3,4]", []int{1, 2, 3, 4}, false},
		{"[-1,0,1]", []int{-1, 0, 1}, false},
		{"[]", []int{}, false},
		{"[1, 2, 3]", []int{1, 2, 3}, false}, // With spaces
		{"[1,a,3]", nil, true},               // Invalid number
		{"1,2,3", []int{1, 2, 3}, false},     // Without brackets
	}
	
	for i, tc := range testCases {
		result, err := parseIntArray(tc.input)
		
		if tc.hasError {
			if err == nil {
				t.Errorf("Test case %d: parseIntArray(%s) expected error but got none", 
					i, tc.input)
			}
		} else {
			if err != nil {
				t.Errorf("Test case %d: parseIntArray(%s) unexpected error: %v", 
					i, tc.input, err)
			} else if !reflect.DeepEqual(result, tc.expected) {
				t.Errorf("Test case %d: parseIntArray(%s) = %v; expected %v", 
					i, tc.input, result, tc.expected)
			}
		}
	}
}

func TestAbs(t *testing.T) {
	testCases := []struct {
		input    int
		expected int
	}{
		{5, 5},
		{-5, 5},
		{0, 0},
		{-1, 1},
		{1, 1},
	}
	
	for i, tc := range testCases {
		result := abs(tc.input)
		if result != tc.expected {
			t.Errorf("Test case %d: abs(%d) = %d; expected %d", 
				i, tc.input, result, tc.expected)
		}
	}
}

// Helper function to sort triplets for consistent comparison
func sortTriplets(triplets [][]int) {
	// Sort each individual triplet
	for _, triplet := range triplets {
		sort.Ints(triplet)
	}
	
	// Sort the slice of triplets
	sort.Slice(triplets, func(i, j int) bool {
		for k := 0; k < 3; k++ {
			if triplets[i][k] != triplets[j][k] {
				return triplets[i][k] < triplets[j][k]
			}
		}
		return false
	})
}

// Benchmark tests
func BenchmarkThreeSumOptimal(b *testing.B) {
	nums := make([]int, 100)
	for i := 0; i < 100; i++ {
		nums[i] = i - 50 // Range from -50 to 49
	}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		threeSumOptimal(nums)
	}
}

func BenchmarkThreeSumBruteForce(b *testing.B) {
	nums := make([]int, 50) // Smaller array for brute force
	for i := 0; i < 50; i++ {
		nums[i] = i - 25 // Range from -25 to 24
	}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		threeSumBruteForce(nums)
	}
}