package formatters

import (
	"io"

	"github.com/cucumber/godog/formatters"
	"github.com/cucumber/godog/internal/storage"
	messages "github.com/cucumber/messages/go/v21"
)

// MultiFormatter passes test progress to multiple formatters.
type MultiFormatter struct {
	formatters []formatter
	repeater   repeater
}

type formatter struct {
	fmt formatters.FormatterFunc
	out io.Writer
}

type repeater []formatters.Formatter

type storageFormatter interface {
	SetStorage(s *storage.Storage)
}

// SetStorage passes storage to all added formatters.
func (r repeater) SetStorage(s *storage.Storage) {
	for _, f := range r {
		if ss, ok := f.(storageFormatter); ok {
			ss.SetStorage(s)
		}
	}
}

// TestRunStarted triggers TestRunStarted for all added formatters.
func (r repeater) TestRunStarted() {
	for _, f := range r {
		f.TestRunStarted()
	}
}

// Feature triggers Feature for all added formatters.
func (r repeater) Feature(document *messages.GherkinDocument, s string, bytes []byte) {
	for _, f := range r {
		f.Feature(document, s, bytes)
	}
}

// Pickle triggers Pickle for all added formatters.
func (r repeater) Pickle(pickle *messages.Pickle) {
	for _, f := range r {
		f.Pickle(pickle)
	}
}

// Defined triggers Defined for all added formatters.
func (r repeater) Defined(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition) {
	for _, f := range r {
		f.Defined(pickle, step, definition)
	}
}

// Failed triggers Failed for all added formatters.
func (r repeater) Failed(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition, err error) {
	for _, f := range r {
		f.Failed(pickle, step, definition, err)
	}
}

// Passed triggers Passed for all added formatters.
func (r repeater) Passed(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition) {
	for _, f := range r {
		f.Passed(pickle, step, definition)
	}
}

// Skipped triggers Skipped for all added formatters.
func (r repeater) Skipped(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition) {
	for _, f := range r {
		f.Skipped(pickle, step, definition)
	}
}

// Undefined triggers Undefined for all added formatters.
func (r repeater) Undefined(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition) {
	for _, f := range r {
		f.Undefined(pickle, step, definition)
	}
}

// Pending triggers Pending for all added formatters.
func (r repeater) Pending(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition) {
	for _, f := range r {
		f.Pending(pickle, step, definition)
	}
}

// Ambiguous triggers Ambiguous for all added formatters.
func (r repeater) Ambiguous(pickle *messages.Pickle, step *messages.PickleStep, definition *formatters.StepDefinition, err error) {
	for _, f := range r {
		f.Ambiguous(pickle, step, definition, err)
	}
}

// Summary triggers Summary for all added formatters.
func (r repeater) Summary() {
	for _, f := range r {
		f.Summary()
	}
}

// Add adds formatter with output writer.
func (m *MultiFormatter) Add(name string, out io.Writer) {
	f := formatters.FindFmt(name)
	if f == nil {
		panic("formatter not found: " + name)
	}

	m.formatters = append(m.formatters, formatter{
		fmt: f,
		out: out,
	})
}

// FormatterFunc implements the FormatterFunc for the multi formatter.
func (m *MultiFormatter) FormatterFunc(suite string, out io.Writer) formatters.Formatter {
	for _, f := range m.formatters {
		out := out
		if f.out != nil {
			out = f.out
		}

		m.repeater = append(m.repeater, f.fmt(suite, out))
	}

	return m.repeater
}
