package main

import (
	"bytes"
	"strings"
	"sync"
	"time"
)

type LogItem struct {
	RunId          string `json:"runId"`
	TimestampMilli int64  `json:"timestampMilli"`
	Commit         string `json:"commit,omitempty"`
	Contents       []byte `json:"contents"`
}

type Logger struct {
	l          sync.Mutex
	runId      string
	commitHash string
	items      []LogItem
	curr       LogItem
}

func NewLogger(runId string) *Logger {
	return &Logger{
		l:     sync.Mutex{},
		runId: runId,
		items: []LogItem{},
		curr: LogItem{
			RunId:    runId,
			Contents: []byte{},
		},
	}
}

func (l *Logger) Write(p []byte) (n int, err error) {
	l.l.Lock()
	defer l.l.Unlock()
	cnt := 0
	// TODO(gio): Reset s.logs periodically
	for len(p) > 0 {
		pos := bytes.Index(p, []byte("\n"))
		if pos != -1 {
			if l.curr.TimestampMilli == 0 {
				l.curr.TimestampMilli = time.Now().UnixMilli()
			}
			l.curr.Contents = append(l.curr.Contents, p[:pos]...)
			l.items = append(l.items, l.curr)
			l.curr = LogItem{
				RunId:    l.runId,
				Commit:   l.commitHash,
				Contents: []byte{},
			}
			p = p[pos+len([]byte("\n")):]
			cnt += pos + len([]byte("\n"))
		} else {
			if l.curr.TimestampMilli == 0 {
				l.curr.TimestampMilli = time.Now().UnixMilli()
			}
			l.curr.Contents = append(l.curr.Contents, p...)
			cnt += len(p)
			p = []byte{}
		}
	}
	return cnt, nil
}

func (l *Logger) Items() []LogItem {
	l.l.Lock()
	defer l.l.Unlock()
	ret := []LogItem{}
	for _, i := range l.items {
		ret = append(ret, i)
	}
	return ret
}

func (l *Logger) Trim(n int) {
	l.l.Lock()
	defer l.l.Unlock()
	l.items = l.items[n:]
}

func (l *Logger) Contents() (string, error) {
	l.l.Lock()
	defer l.l.Unlock()
	var ret strings.Builder
	for _, i := range l.items {
		if _, err := ret.Write(i.Contents); err != nil {
			return "", err
		}
		if _, err := ret.WriteString("\n"); err != nil {
			return "", err
		}
	}
	return ret.String(), nil
}
