package redo

import (
	"fmt"
	"hash/crc32"
	"io"
	"os"

	"gordenko.dev/dima/diploma/bin"
)

type REDOFile struct {
	MetricID         uint32
	Timestamp        uint32
	Value            float64
	IsDataPageReused bool
	DataPage         PageToWrite
	IsRootChanged    bool
	RootPageNo       uint32
	ReusedIndexPages []uint32
	IndexPages       []PageToWrite
}

type ReadREDOFileReq struct {
	FileName      string
	DataPageSize  int
	IndexPageSize int
}

func ReadREDOFile(req ReadREDOFileReq) (*REDOFile, error) {
	buf, err := os.ReadFile(req.FileName)
	if err != nil {
		return nil, err
	}

	if len(buf) < 25 {
		return nil, io.EOF
	}

	var (
		end                = len(buf) - 4
		payload            = buf[:end]
		checksum           = bin.GetUint32(buf[end:])
		calculatedChecksum = crc32.ChecksumIEEE(payload)
	)

	// Помилка чексуми означає що файл або недописаний, або пошкодженний
	if checksum != calculatedChecksum {
		return nil, fmt.Errorf("written checksum %d not equal calculated checksum %d",
			checksum, calculatedChecksum)
	}

	var (
		redoLog = REDOFile{
			MetricID:         bin.GetUint32(buf[0:]),
			Timestamp:        bin.GetUint32(buf[4:]),
			Value:            bin.GetFloat64(buf[8:]),
			IsDataPageReused: buf[16] == 1,
			DataPage: PageToWrite{
				PageNo: bin.GetUint32(buf[17:]),
				Data:   buf[21 : 21+req.DataPageSize],
			},
		}
		pos = 21 + req.DataPageSize
	)

	for {
		if pos == len(payload) {
			return &redoLog, nil
		}

		if pos > len(payload) {
			return nil, io.EOF
		}

		flags := buf[pos]

		item := PageToWrite{
			PageNo: bin.GetUint32(buf[pos+1:]),
		}
		pos += 5 // flags + pageNo
		item.Data = buf[pos : pos+req.IndexPageSize]
		pos += req.IndexPageSize

		redoLog.IndexPages = append(redoLog.IndexPages, item)

		if (flags & FlagReused) == FlagReused {
			redoLog.ReusedIndexPages = append(redoLog.ReusedIndexPages, item.PageNo)
		}

		if (flags & FlagNewRoot) == FlagNewRoot {
			redoLog.IsRootChanged = true
			redoLog.RootPageNo = item.PageNo
		}
	}
}
