package gojunitreport

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"time"

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

type parser interface {
	Parse(r io.Reader) (gtr.Report, error)
	Events() []gotest.Event
}

// Config contains the go-junit-report command configuration.
type Config struct {
	Parser        string
	Hostname      string
	PackageName   string
	SkipXMLHeader bool
	SubtestMode   gotest.SubtestMode
	Properties    map[string]string
	TimestampFunc func() time.Time

	// For debugging
	PrintEvents bool
}

// Run runs the go-junit-report command and returns the generated report.
func (c Config) Run(input io.Reader, output io.Writer) (*gtr.Report, error) {
	var p parser
	switch c.Parser {
	case "gotest":
		p = gotest.NewParser(c.gotestOptions()...)
	case "gojson":
		p = gotest.NewJSONParser(c.gotestOptions()...)
	default:
		return nil, fmt.Errorf("invalid parser: %s", c.Parser)
	}

	report, err := p.Parse(input)
	if err != nil {
		return nil, fmt.Errorf("error parsing input: %w", err)
	}

	if c.PrintEvents {
		enc := json.NewEncoder(os.Stderr)
		for _, event := range p.Events() {
			if err := enc.Encode(event); err != nil {
				return nil, err
			}
		}
	}

	for i := range report.Packages {
		for k, v := range c.Properties {
			report.Packages[i].SetProperty(k, v)
		}
	}

	if err = c.writeXML(output, report); err != nil {
		return nil, err
	}
	return &report, nil
}

func (c Config) writeXML(w io.Writer, report gtr.Report) error {
	testsuites := junit.CreateFromReport(report, c.Hostname)

	if !c.SkipXMLHeader {
		_, err := fmt.Fprintf(w, xml.Header)
		if err != nil {
			return err
		}
	}

	enc := xml.NewEncoder(w)
	enc.Indent("", "\t")
	if err := enc.Encode(testsuites); err != nil {
		return err
	}
	if err := enc.Flush(); err != nil {
		return err
	}
	_, err := fmt.Fprintf(w, "\n")
	return err
}

func (c Config) gotestOptions() []gotest.Option {
	return []gotest.Option{
		gotest.PackageName(c.PackageName),
		gotest.SetSubtestMode(c.SubtestMode),
		gotest.TimestampFunc(c.TimestampFunc),
	}
}