package decryption

import (
	"bytes"
	"erupe-ce/common/byteframe"
	"io"
	"testing"
)

func TestUnpackSimple_UncompressedData(t *testing.T) {
	// Test data that doesn't have JPK header - should be returned as-is
	input := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}
	result := UnpackSimple(input)

	if !bytes.Equal(result, input) {
		t.Errorf("UnpackSimple() with uncompressed data should return input as-is, got %v, want %v", result, input)
	}
}

func TestUnpackSimple_InvalidHeader(t *testing.T) {
	// Test data with wrong header
	input := []byte{0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x02, 0x03, 0x04}
	result := UnpackSimple(input)

	if !bytes.Equal(result, input) {
		t.Errorf("UnpackSimple() with invalid header should return input as-is, got %v, want %v", result, input)
	}
}

func TestUnpackSimple_JPKHeaderWrongType(t *testing.T) {
	// Test JPK header but wrong type (not type 3)
	bf := byteframe.NewByteFrame()
	bf.SetLE()
	bf.WriteUint32(0x1A524B4A) // JPK header
	bf.WriteUint16(0x00)       // Reserved
	bf.WriteUint16(1)          // Type 1 instead of 3
	bf.WriteInt32(12)          // Start offset
	bf.WriteInt32(10)          // Out size

	result := UnpackSimple(bf.Data())
	// Should return the input as-is since it's not type 3
	if !bytes.Equal(result, bf.Data()) {
		t.Error("UnpackSimple() with non-type-3 JPK should return input as-is")
	}
}

func TestUnpackSimple_ValidJPKType3_EmptyData(t *testing.T) {
	// Create a valid JPK type 3 header with minimal compressed data
	bf := byteframe.NewByteFrame()
	bf.SetLE()
	bf.WriteUint32(0x1A524B4A) // JPK header "JKR\x1A"
	bf.WriteUint16(0x00)       // Reserved
	bf.WriteUint16(3)          // Type 3
	bf.WriteInt32(12)          // Start offset (points to byte 12, after header)
	bf.WriteInt32(0)           // Out size (empty output)

	result := UnpackSimple(bf.Data())
	// Should return empty buffer
	if len(result) != 0 {
		t.Errorf("UnpackSimple() with zero output size should return empty slice, got length %d", len(result))
	}
}

func TestUnpackSimple_JPKHeader(t *testing.T) {
	// Test that the function correctly identifies JPK header (0x1A524B4A = "JKR\x1A" in little endian)
	bf := byteframe.NewByteFrame()
	bf.SetLE()
	bf.WriteUint32(0x1A524B4A) // Correct JPK magic

	data := bf.Data()
	if len(data) < 4 {
		t.Fatal("Not enough data written")
	}

	// Verify the header bytes are correct
	_, _ = bf.Seek(0, io.SeekStart)
	header := bf.ReadUint32()
	if header != 0x1A524B4A {
		t.Errorf("Header = 0x%X, want 0x1A524B4A", header)
	}
}

func TestJPKBitShift_Initialization(t *testing.T) {
	// Test that bitShift correctly initializes from zero state
	bf := byteframe.NewByteFrame()
	bf.WriteUint8(0xFF) // All bits set
	bf.WriteUint8(0x00) // No bits set

	_, _ = bf.Seek(0, io.SeekStart)
	s := &jpkState{}

	// First call should read 0xFF as flag and return bit 7 = 1
	bit := s.bitShift(bf)
	if bit != 1 {
		t.Errorf("bitShift() first bit of 0xFF = %d, want 1", bit)
	}
}

func TestUnpackSimple_ConcurrentSafety(t *testing.T) {
	// Verify that concurrent UnpackSimple calls don't race.
	// Non-JPK data is returned as-is; the important thing is no data race.
	input := []byte{0x00, 0x01, 0x02, 0x03}

	done := make(chan struct{})
	for i := 0; i < 8; i++ {
		go func() {
			defer func() { done <- struct{}{} }()
			for j := 0; j < 100; j++ {
				result := UnpackSimple(input)
				if !bytes.Equal(result, input) {
					t.Errorf("concurrent UnpackSimple returned wrong data")
				}
			}
		}()
	}
	for i := 0; i < 8; i++ {
		<-done
	}
}

func TestReadByte(t *testing.T) {
	bf := byteframe.NewByteFrame()
	bf.WriteUint8(0x42)
	bf.WriteUint8(0xAB)

	_, _ = bf.Seek(0, io.SeekStart)
	b1 := ReadByte(bf)
	b2 := ReadByte(bf)

	if b1 != 0x42 {
		t.Errorf("ReadByte() = 0x%X, want 0x42", b1)
	}
	if b2 != 0xAB {
		t.Errorf("ReadByte() = 0x%X, want 0xAB", b2)
	}
}

func TestJPKCopy(t *testing.T) {
	outBuffer := make([]byte, 20)
	// Set up some initial data
	outBuffer[0] = 'A'
	outBuffer[1] = 'B'
	outBuffer[2] = 'C'

	index := 3
	// Copy 3 bytes from offset 2 (looking back 2+1=3 positions)
	JPKCopy(outBuffer, 2, 3, &index)

	// Should have copied 'A', 'B', 'C' to positions 3, 4, 5
	if outBuffer[3] != 'A' || outBuffer[4] != 'B' || outBuffer[5] != 'C' {
		t.Errorf("JPKCopy failed: got %v at positions 3-5, want ['A', 'B', 'C']", outBuffer[3:6])
	}
	if index != 6 {
		t.Errorf("index = %d, want 6", index)
	}
}

func TestJPKCopy_OverlappingCopy(t *testing.T) {
	// Test copying with overlapping regions (common in LZ-style compression)
	outBuffer := make([]byte, 20)
	outBuffer[0] = 'X'

	index := 1
	// Copy from 1 position back, 5 times - should repeat the pattern
	JPKCopy(outBuffer, 0, 5, &index)

	// Should produce: X X X X X (repeating X)
	for i := 1; i < 6; i++ {
		if outBuffer[i] != 'X' {
			t.Errorf("outBuffer[%d] = %c, want 'X'", i, outBuffer[i])
		}
	}
	if index != 6 {
		t.Errorf("index = %d, want 6", index)
	}
}

func TestProcessDecode_EmptyOutput(t *testing.T) {
	bf := byteframe.NewByteFrame()
	bf.WriteUint8(0x00)

	outBuffer := make([]byte, 0)
	// Should not panic with empty output buffer
	ProcessDecode(bf, outBuffer)
}

func TestUnpackSimple_EdgeCases(t *testing.T) {
	// Test with data that has at least 4 bytes (header size required)
	tests := []struct {
		name  string
		input []byte
	}{
		{
			name:  "four bytes non-JPK",
			input: []byte{0x00, 0x01, 0x02, 0x03},
		},
		{
			name:  "partial header padded",
			input: []byte{0x4A, 0x4B, 0x00, 0x00},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := UnpackSimple(tt.input)
			// Should return input as-is without crashing
			if !bytes.Equal(result, tt.input) {
				t.Errorf("UnpackSimple() = %v, want %v", result, tt.input)
			}
		})
	}
}

func BenchmarkUnpackSimple_Uncompressed(b *testing.B) {
	data := make([]byte, 1024)
	for i := range data {
		data[i] = byte(i % 256)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = UnpackSimple(data)
	}
}

func BenchmarkUnpackSimple_JPKHeader(b *testing.B) {
	bf := byteframe.NewByteFrame()
	bf.SetLE()
	bf.WriteUint32(0x1A524B4A) // JPK header
	bf.WriteUint16(0x00)
	bf.WriteUint16(3)
	bf.WriteInt32(12)
	bf.WriteInt32(0)
	data := bf.Data()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = UnpackSimple(data)
	}
}

func BenchmarkReadByte(b *testing.B) {
	bf := byteframe.NewByteFrame()
	for i := 0; i < 1000; i++ {
		bf.WriteUint8(byte(i % 256))
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = bf.Seek(0, io.SeekStart)
		_ = ReadByte(bf)
	}
}
