package main

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type LogEntry struct {
	Timestamp string      `json:"timestamp"`
	Protocol  string      `json:"protocol"`
	Type      string      `json:"type"`
	Summary   string      `json:"summary"`
	Data      interface{} `json:"data"`
}

func main() {
	// Hardcode for reliability
	logFile := "session_20260107_174406.jsonl"

	if logFile == "" {
		panic("No session log found")
	}

	fmt.Printf("Reading %s...\n", logFile)
	f, err := os.Open(logFile)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	// Larger buffer for huge lines
	buf := make([]byte, 1024*1024)
	scanner.Buffer(buf, 1024*1024)

	for scanner.Scan() {
		var entry LogEntry
		if err := json.Unmarshal(scanner.Bytes(), &entry); err != nil {
			continue
		}

		// Looking for the huge packet
		// Summary: "Packet 1 | Req 256 | Len 24182" (or similar length)
		if entry.Protocol == "TCP" && strings.Contains(entry.Summary, "Len") {
			// Check if Data has hex
			dataMap, ok := entry.Data.(map[string]interface{})
			if !ok {
				continue
			}
			hexData, ok := dataMap["hex"].(string)
			if !ok || len(hexData) < 20000 { // Look for huge payload
				continue
			}

			fmt.Println("Found candidate packet:", entry.Summary)
			packetBytes, err := hex.DecodeString(hexData)
			if err != nil {
				panic(err)
			}

			// Parse wrapper
			// We assume the payload of the wrapper is the Message
			// Wrapper: [Tag1:PacketID] [Tag3:MessageBytes]
			// We want MessageBytes.
			// Simple parser: skip Tag1/Val1. Find Tag3 (0x1a).
			// 08 02 (Packet ID 2) -> 2 bytes.
			// 1a (Tag 3) -> 1 byte.
			// Length (Varint).

			// Heuristic search for 1a and length
			// Or simple protobuf reader
			r := bytes.NewReader(packetBytes)
			// Skip Field 1 (08 xx)
			b, _ := r.ReadByte()
			if b == 0x08 {
				// Read varint
				for {
					b, _ := r.ReadByte()
					if b < 0x80 {
						break
					}
				}
			} else {
				// Might be diff packet ID? Log said Packet 1. 08 01.
				fmt.Println("Warning: Expected 0x08 tag, got", b)
				// Reset and try to find 0x1a anyway
				r.Seek(0, 0)
			}

			// Read Tag
			for {
				b, err := r.ReadByte()
				if err != nil {
					break
				}
				if b == 0x1a { // Field 3
					// This is likely the payload
					fmt.Println("Found Field 3 tag (Message Payload)")

					// Decode length
					lenVal := uint64(0)
					shift := uint(0)
					for {
						b, _ := r.ReadByte()
						lenVal |= uint64(b&0x7F) << shift
						if b < 0x80 {
							break
						}
						shift += 7
					}
					fmt.Printf("Payload Length: %d\n", lenVal)

					payload := make([]byte, lenVal)
					n, _ := r.Read(payload)
					if uint64(n) != lenVal {
						fmt.Println("Warning: Short read")
					}

					// Now we have the Message (RequestNumber + GameStatusReportRequest).
					// RequestNumber is Field 1 (08 80 04 -> 512).
					// GameStatusReportRequest is Field 512 (Tag 82 20 -> 1002).
					// We need the value of Field 512.

					r2 := bytes.NewReader(payload)

					for {
						tag, err := readVarint(r2)
						if err != nil {
							break
						}
						fieldNum := tag >> 3
						wireType := tag & 7

						if fieldNum == 512 {
							fmt.Println("Found Field 512 (GameStatusReportRequest)")
							// Read length
							msgLen, _ := readVarint(r2)
							msgBytes := make([]byte, msgLen)
							r2.Read(msgBytes)

							// msgBytes is GameStatusReportRequest.
							// It contains status_report (Field 1).
							// We want to save *msgBytes* so we can inject it into a new Message struct?
							// Or extract StatusReport payload?

							// The user wants to set GameStatusReportRequest.StatusReport.
							// So check inside msgBytes for Field 1.
							r3 := bytes.NewReader(msgBytes)
							tag3, _ := readVarint(r3)
							if (tag3 >> 3) == 1 {
								fmt.Println("Found Field 1 (StatusReport)")
								srLen, _ := readVarint(r3)
								srBytes := make([]byte, srLen)
								r3.Read(srBytes)

								// Wait, is StatusReport just one field?
								// The log showed StatusReport had GameID, Status, Players...
								// Ah, GameStatusReportRequest message only has ONE field: `status_report`.
								// So `msgBytes` is the wire bytes for `GameStatusReportRequest`.
								// If we assume `main.go` has `GameStatusReportRequest` struct, we can unmarshal `msgBytes` into it.
								// YES.

								err = ioutil.WriteFile("mock_gamestatus.bin", msgBytes, 0644)
								if err != nil {
									panic(err)
								}
								fmt.Println("Saved mock_gamestatus.bin")
								return
							}
						} else {
							// Skip
							skip(r2, wireType)
						}
					}
				}
			}
		}
	}
}

func readVarint(r *bytes.Reader) (uint64, error) {
	x := uint64(0)
	s := uint(0)
	for {
		b, err := r.ReadByte()
		if err != nil {
			return 0, err
		}
		x |= uint64(b&0x7F) << s
		if b < 0x80 {
			return x, nil
		}
		s += 7
	}
}

func skip(r *bytes.Reader, wireType uint64) {
	switch wireType {
	case 0:
		readVarint(r)
	case 1:
		r.Seek(8, 1)
	case 2:
		l, _ := readVarint(r)
		r.Seek(int64(l), 1)
	case 5:
		r.Seek(4, 1)
	}
}
