package main

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"text/tabwriter"

	"igit.com/xbase/raft/db"
)

const (
	RecordTypePut    = 0x01
	RecordTypeDelete = 0x02
	// CRC(4) + Type(1) + KeyLen(2) + ValLen(4) + CommitIndex(8)
	HeaderSize = 19
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run main.go <data_dir>")
		return
	}
	dataDir := os.Args[1]
	dataFile := dataDir + "/values.data"

	f, err := os.Open(dataFile)
	if err != nil {
		fmt.Printf("Error opening file: %v\n", err)
		return
	}
	defer f.Close()

	fmt.Printf("Scanning storage file: %s\n\n", dataFile)

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
	fmt.Fprintln(w, "Offset\tType\tCRC\tKeyLen\tValLen\tCommit\tKey\tValue")
	fmt.Fprintln(w, "------\t----\t---\t------\t------\t------\t---\t-----")

	offset := int64(0)
	reader := f

	for {
		// Read Header
		header := make([]byte, HeaderSize)
		if _, err := reader.ReadAt(header, offset); err != nil {
			if err == io.EOF {
				break
			}
			// If we hit EOF during read, it might be partial read at end
			if n, _ := reader.ReadAt(header, offset); n == 0 {
				break
			}
			fmt.Printf("Error reading header at offset %d: %v\n", offset, err)
			break
		}

		// Parse Header
		crc := binary.LittleEndian.Uint32(header[0:4])
		recType := header[4]
		keyLen := binary.LittleEndian.Uint16(header[5:7])
		valLen := binary.LittleEndian.Uint32(header[7:11])
		commitIndex := binary.LittleEndian.Uint64(header[11:19])

		typeStr := "PUT"
		if recType == RecordTypeDelete {
			typeStr = "DEL"
		} else if recType != RecordTypePut {
			typeStr = fmt.Sprintf("UNK(%d)", recType)
		}

		// Read Data (Key + Value)
		totalDataLen := int(keyLen) + int(valLen)
		data := make([]byte, totalDataLen)
		if _, err := reader.ReadAt(data, offset+HeaderSize); err != nil {
			fmt.Printf("Error reading data at offset %d: %v\n", offset+HeaderSize, err)
			break
		}

		key := string(data[:keyLen])
		val := string(data[keyLen:])

		// Truncate long values for display
		displayVal := val
		if len(displayVal) > 30 {
			displayVal = displayVal[:27] + "..."
		}
		if recType == RecordTypeDelete {
			displayVal = "<tombstone>"
		}

		fmt.Fprintf(w, "%d\t%s\t%08X\t%d\t%d\t%d\t%s\t%q\n",
			offset, typeStr, crc, keyLen, valLen, commitIndex, key, displayVal)

		offset += int64(HeaderSize + totalDataLen)
	}
	w.Flush()
	fmt.Println()

	// Also try to open Engine to verify Index Rebuild
	e, err := db.NewEngine(dataDir)
	if err == nil {
		fmt.Println("Engine Load Check: SUCCESS (Index rebuilt from disk)")
		e.Close()
	} else {
		fmt.Printf("Engine Load Check: FAILED (%v)\n", err)
	}
}
