package wal

import (
	"encoding/binary"
	"errors"
	"hash/crc32"
)

const (
	walFileHeaderSize = 32
	walMagic          = 0x57414C4B // "WALK" in ASCII
	walFormatVersion  = 1
)

// WalFileHeader is the 32-byte header written at the start of every WAL segment file.
type WalFileHeader struct {
	Magic         uint32
	FormatVersion uint16
	HeaderSize    uint16
	BlockSize     uint32
	SegmentID     uint64
	StartSequence uint64
	HeaderCRC     uint32
}

// EncodeWalHeader serializes h into a fixed-size 32-byte array using little-endian byte order.
// The HeaderCRC field is computed over bytes 0–27 (everything except the CRC itself).
func EncodeWalHeader(h *WalFileHeader) [walFileHeaderSize]byte {
	h.HeaderSize = walFileHeaderSize
	h.Magic = walMagic
	h.FormatVersion = walFormatVersion

	var buf [walFileHeaderSize]byte
	le := binary.LittleEndian

	le.PutUint32(buf[0:4], h.Magic)
	le.PutUint16(buf[4:6], h.FormatVersion)
	le.PutUint16(buf[6:8], h.HeaderSize)
	le.PutUint32(buf[8:12], h.BlockSize)
	le.PutUint64(buf[12:20], h.SegmentID)
	le.PutUint64(buf[20:28], h.StartSequence)

	// CRC32 IEEE over bytes 0–27 (excludes the CRC field itself)
	h.HeaderCRC = crc32.ChecksumIEEE(buf[0:28])
	le.PutUint32(buf[28:32], h.HeaderCRC)

	return buf
}

var (
	errBadMagic      = errors.New("wal: bad magic number")
	errBadVersion    = errors.New("wal: unsupported format version")
	errBadHeaderSize = errors.New("wal: bad header size")
	errCRCMismatch   = errors.New("wal: header CRC mismatch")
	errHeaderTooShort = errors.New("wal: header data too short")
)

// DecodeWalHeader parses a 32-byte little-endian header and validates magic,
// format version, header size, and CRC.
func DecodeWalHeader(data []byte) (*WalFileHeader, error) {
	if len(data) < walFileHeaderSize {
		return nil, errHeaderTooShort
	}

	le := binary.LittleEndian

	magic := le.Uint32(data[0:4])
	if magic != walMagic {
		return nil, errBadMagic
	}

	version := le.Uint16(data[4:6])
	if version != walFormatVersion {
		return nil, errBadVersion
	}

	hdrSize := le.Uint16(data[6:8])
	if hdrSize != walFileHeaderSize {
		return nil, errBadHeaderSize
	}

	// Verify CRC before trusting any other fields
	gotCRC := crc32.ChecksumIEEE(data[0:28])
	storedCRC := le.Uint32(data[28:32])
	if gotCRC != storedCRC {
		return nil, errCRCMismatch
	}

	return &WalFileHeader{
		Magic:         magic,
		FormatVersion: version,
		HeaderSize:    hdrSize,
		BlockSize:     le.Uint32(data[8:12]),
		SegmentID:     le.Uint64(data[12:20]),
		StartSequence: le.Uint64(data[20:28]),
		HeaderCRC:     storedCRC,
	}, nil
}
