// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: BUSL-1.1

package bsr

import (
	"context"
	"fmt"
)

// sizes
const (
	lengthSize    = 4
	protocolSize  = 4
	chunkTypeSize = 4
	directionSize = 1

	crcDataSize   = protocolSize + chunkTypeSize + directionSize + timestampSize
	chunkBaseSize = lengthSize + crcDataSize
	crcSize       = 4

	// MaxChunkDataLength sets an upper bound on BSR chunk lengths
	// Default to 64MB, as this is the limit for protobufs, which back our chunks
	// TODO: Should this be configurable as an option?
	MaxChunkDataLength = 64 * 100 * 1000
)

// Chunk Types
const (
	ChunkHeader ChunkType = "HEAD"
	ChunkEnd    ChunkType = "DONE"
)

// ChunkType identifies the type of a chunk.
type ChunkType string

// ValidChunkType checks ifa given ChunkType is valid.
func ValidChunkType(c ChunkType) bool {
	return len(c) <= chunkTypeSize
}

// Chunk is a section of a bsr data file.
type Chunk interface {
	// GetLength returns the length of the chunk data.
	GetLength() uint32
	// GetProtocol returns the protocol of the recorded data.
	GetProtocol() Protocol
	// GetType returns the chunk type.
	GetType() ChunkType
	// GetDirection returns the direction of the data in the chunk.
	GetDirection() Direction
	// GetTimestamp returns the timestamp of a Chunk.
	GetTimestamp() *Timestamp

	// MarshalData serializes the data portion of a chunk.
	MarshalData(context.Context) ([]byte, error)
}

// BaseChunk contains the common fields of all chunk types.
type BaseChunk struct {
	Protocol  Protocol
	Direction Direction
	Timestamp *Timestamp
	Type      ChunkType

	length uint32
}

// NewBaseChunk creates a BaseChunk.
func NewBaseChunk(ctx context.Context, p Protocol, d Direction, t *Timestamp, typ ChunkType) (*BaseChunk, error) {
	const op = "bsr.NewBaseChunk"
	if !ValidProtocol(p) {
		return nil, fmt.Errorf("%s: protocol name cannot be greater than 4 characters: %w", op, ErrInvalidParameter)
	}
	if !ValidDirection(d) {
		return nil, fmt.Errorf("%s: invalid direction: %w", op, ErrInvalidParameter)
	}
	if t == nil {
		return nil, fmt.Errorf("%s: timestamp must not be nil: %w", op, ErrInvalidParameter)
	}
	if !ValidChunkType(typ) {
		return nil, fmt.Errorf("%s: chunk type cannot be greater than 4 characters: %w", op, ErrInvalidParameter)
	}

	return &BaseChunk{
		Protocol:  p,
		Direction: d,
		Timestamp: t,
		Type:      typ,
	}, nil
}

// GetLength returns the length of the chunk data.
func (b *BaseChunk) GetLength() uint32 {
	return b.length
}

// GetProtocol returns the protocol of the recorded data.
func (b *BaseChunk) GetProtocol() Protocol {
	return b.Protocol
}

// GetType returns the chunk type.
func (b *BaseChunk) GetType() ChunkType {
	return b.Type
}

// GetDirection returns the direction of the data in the chunk.
func (b *BaseChunk) GetDirection() Direction {
	return b.Direction
}

// GetTimestamp returns the timestamp of a Chunk.
func (b *BaseChunk) GetTimestamp() *Timestamp {
	return b.Timestamp
}
