package dag

import (
	"strings"

	"github.com/gookit/color"
	"k8s.io/apimachinery/pkg/labels"

	"github.com/tidbcloud/eks-provider/tests/e2e/label"
)

type Reporter interface {
	AddEnv(envs ...label.Object[Env])
	AddCase(env string, cs ...label.Object[Case])
	Summary() *Summary
}

type Summary struct {
	Total   int
	Succeed int
	Failed  int
	Skipped int

	Reports []EnvReport
}

type EnvReport struct {
	Desc   string
	Labels labels.Labels
	Env    string

	ParentEnvs []string
	Cases      []CaseReport

	State label.State

	FailedError error
}

type CaseReport struct {
	Desc   string
	Labels labels.Labels

	State label.State
}

func NewReporter() Reporter {
	return &reporter{
		cases: map[string][]label.Object[Case]{},
	}
}

type reporter struct {
	envs  []label.Object[Env]
	cases map[string][]label.Object[Case]
}

func (r *reporter) AddEnv(es ...label.Object[Env]) {
	r.envs = append(r.envs, es...)
}

func (r *reporter) AddCase(env string, cases ...label.Object[Case]) {
	cs := r.cases[env]
	cs = append(cs, cases...)
	r.cases[env] = cs
}

func (r *reporter) Summary() *Summary {
	s := Summary{}
	for _, e := range r.envs {
		er := EnvReport{
			Desc:        e.GetDesc(),
			Labels:      e.GetLabels(),
			Env:         e.Get().Name(),
			ParentEnvs:  e.Get().Envs(),
			State:       e.GetState(),
			FailedError: e.Error(),
		}
		cases := r.cases[e.Get().Name()]
		for _, c := range cases {
			er.Cases = append(er.Cases, CaseReport{
				Desc:   c.GetDesc(),
				Labels: c.GetLabels(),
				State:  c.GetState(),
			})
			s.Total++
			switch c.GetState() {
			case label.StateFailed:
				s.Failed++
			case label.StateSkipped:
				s.Skipped++
			case label.StateSucceed:
				s.Succeed++
			default:
			}

		}
		s.Reports = append(s.Reports, er)
	}

	return &s
}

func (s *Summary) String() string {
	b := strings.Builder{}

	white := color.HEXStyle("#f8f8f0")
	white.SetOpts(color.Opts{color.OpBold})
	red := color.HEXStyle("#e95678")
	red.SetOpts(color.Opts{color.OpBold})
	green := color.HEXStyle("#a6e22e")
	green.SetOpts(color.Opts{color.OpBold})
	orange := color.HEXStyle("#fd971f")
	orange.SetOpts(color.Opts{color.OpBold})

	b.WriteByte('\n')
	b.WriteString(white.Sprintf("Total: %v\t", s.Total))
	b.WriteString(green.Sprintf("Succeed: %v\t", s.Succeed))
	b.WriteString(red.Sprintf("Failed: %v\t", s.Failed))
	b.WriteString(orange.Sprintf("Skipped: %v\n", s.Skipped))
	b.WriteByte('\n')

	for _, r := range s.Reports {
		switch r.State {
		case label.StateFailed:
			b.WriteString(red.Sprintf("[ENV] %s is failed to setup: %v\n", r.Env, r.FailedError))
		case label.StateSkipped:
			b.WriteString(orange.Sprintf("[ENV] %s is skipped\n", r.Env))
		}
	}

	return b.String()
}
