package gotest

import (
	"bufio"
	"encoding/json"
	"io"
	"time"

	"github.com/jstemmer/go-junit-report/v2/gtr"
)

// NewJSONParser returns a new Go test json output parser.
func NewJSONParser(options ...Option) *JSONParser {
	return &JSONParser{gp: NewParser(options...)}
}

// JSONParser is a `go test -json` output Parser.
type JSONParser struct {
	gp *Parser
}

// Parse parses Go test json output from the given io.Reader r and returns
// gtr.Report.
func (p *JSONParser) Parse(r io.Reader) (gtr.Report, error) {
	return p.gp.Parse(newJSONReader(r))
}

// Events returns the events created by the parser.
func (p *JSONParser) Events() []Event {
	return p.gp.Events()
}

type jsonEvent struct {
	Time    time.Time
	Action  string
	Package string
	Test    string
	Elapsed float64 // seconds
	Output  string
}

type jsonReader struct {
	r   *bufio.Reader
	buf []byte
}

func newJSONReader(reader io.Reader) *jsonReader {
	return &jsonReader{r: bufio.NewReader(reader)}
}

func (j *jsonReader) Read(p []byte) (int, error) {
	var err error
	for len(j.buf) == 0 {
		j.buf, err = j.readNextLine()
		if err != nil {
			return 0, err
		}
	}
	n := copy(p, j.buf)
	j.buf = j.buf[n:]
	return n, nil
}

func (j jsonReader) readNextLine() ([]byte, error) {
	line, err := j.r.ReadBytes('\n')
	if err != nil {
		return nil, err
	}
	if len(line) == 0 || line[0] != '{' {
		return line, nil
	}
	var event jsonEvent
	if err := json.Unmarshal(line, &event); err != nil {
		return nil, err
	}
	return []byte(event.Output), nil
}