package mfer

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

	"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 {
		e := m.generateInner()
		if e != nil {
			return e
		}
	}
	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)
	gzw, err := gzip.NewWriterLevel(idc, gzip.BestCompression)
	if err != nil {
		return err
	}
	_, err = gzw.Write(innerData)
	if err != nil {
		return err
	}

	gzw.Close()

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

func (m *manifest) generateInner() error {
	m.pbInner = &MFFile{
		Version:   MFFile_VERSION_ONE,
		CreatedAt: newTimestampFromTime(time.Now()),
		Files:     []*MFFilePath{},
	}
	for _, f := range m.files {
		nf := &MFFilePath{
			Path: f.path,
			// FIXME add more stuff
		}
		m.pbInner.Files = append(m.pbInner.Files, nf)
	}
	return nil
}
