package mfer

import (
	"bytes"
	"crypto/sha256"
	"errors"
	"time"

	"github.com/klauspost/compress/zstd"
	"google.golang.org/protobuf/proto"
)

// MAGIC is the file format magic bytes prefix (rot13 of "MANIFEST").
const MAGIC string = "ZNAVSRFG"

func newTimestampFromTime(t time.Time) *Timestamp {
	out := &Timestamp{
		Seconds: t.Unix(),
		Nanos:   int32(t.UnixNano() - (t.Unix() * 1000000000)),
	}
	return out
}

func (m *manifest) generate() error {
	if m.pbInner == nil {
		return errors.New("internal error: pbInner not set")
	}
	if m.pbOuter == nil {
		e := m.generateOuter()
		if e != nil {
			return e
		}
	}
	dat, err := proto.MarshalOptions{Deterministic: true}.Marshal(m.pbOuter)
	if err != nil {
		return err
	}
	m.output = bytes.NewBuffer([]byte(MAGIC))
	_, err = m.output.Write(dat)
	if err != nil {
		return err
	}
	return nil
}

func (m *manifest) generateOuter() error {
	if m.pbInner == nil {
		return errors.New("internal error")
	}
	innerData, err := proto.MarshalOptions{Deterministic: true}.Marshal(m.pbInner)
	if err != nil {
		return err
	}

	h := sha256.New()
	h.Write(innerData)

	idc := new(bytes.Buffer)
	zw, err := zstd.NewWriter(idc, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
	if err != nil {
		return err
	}
	_, err = zw.Write(innerData)
	if err != nil {
		return err
	}

	_ = zw.Close()

	o := &MFFileOuter{
		InnerMessage:    idc.Bytes(),
		Size:            int64(len(innerData)),
		Sha256:          h.Sum(nil),
		Version:         MFFileOuter_VERSION_ONE,
		CompressionType: MFFileOuter_COMPRESSION_ZSTD,
	}
	m.pbOuter = o
	return nil
}
