package view

import (
	"fmt"
	"io"
	"slices"
	"strings"

	"github.com/go-echarts/go-echarts/v2/charts"
	"github.com/go-echarts/go-echarts/v2/opts"
)

type Chart interface {
	AddX(interface{})
	AddY(string, []int)
	Render(io.Writer) error
	Overlap(Chart)
}

func NewChart(name string) Chart {
	switch name {
	case "line":
		return NewLine()
	case "bar":
		return NewBar()
	case "stack":
		return NewStack()
	case "pie":
		return NewPie()
	default:
		panic("bad chart name " + name)
	}
}

type Line struct {
	*charts.Line
}

func NewLine() Line {
	return Line{Line: charts.NewLine()}
}

func (line Line) AddX(v interface{}) {
	line.SetXAxis(v)
}

func (line Line) AddY(name string, v []int) {
	y := make([]opts.LineData, len(v))
	for i := range y {
		y[i].Value = v[i]
	}
	line.AddSeries(name, y).
		SetSeriesOptions(charts.WithBarChartOpts(opts.BarChart{
			Stack: "stackB",
		}))
}

func (line Line) Overlap(other Chart) {
	overlapper, ok := other.(charts.Overlaper)
	if !ok {
		panic(fmt.Sprintf("cannot overlap %T", other))
	}
	line.Line.Overlap(overlapper)
}

type Bar struct {
	*charts.Bar
}

func NewBar() Bar {
	return Bar{Bar: charts.NewBar()}
}

func (bar Bar) AddX(v interface{}) {
	bar.SetXAxis(v)
}

func (bar Bar) AddY(name string, v []int) {
	y := make([]opts.BarData, len(v))
	for i := range v {
		y[i].Value = v[i]
	}
	bar.AddSeries(name, y)
}

func (bar Bar) Overlap(other Chart) {
	overlapper, ok := other.(charts.Overlaper)
	if !ok {
		panic(fmt.Sprintf("cannot overlap %T", other))
	}
	bar.Bar.Overlap(overlapper)
}

type Stack struct {
	Bar
}

func NewStack() Stack {
	bar := NewBar()
	bar.SetSeriesOptions(charts.WithBarChartOpts(opts.BarChart{Stack: "x"}))
	return Stack{Bar: bar}
}

func (stack Stack) AddY(name string, v []int) {
	y := make([]opts.BarData, len(v))
	for i := range v {
		y[i].Value = v[i]
	}
	stack.AddSeries(name, y).
		SetSeriesOptions(charts.WithBarChartOpts(opts.BarChart{
			Stack: "stackA",
		}))
}

type Pie struct {
	*charts.Pie
	series []opts.PieData
}

func NewPie() *Pie {
	return &Pie{Pie: charts.NewPie()}
}

func (pie *Pie) AddX(v interface{}) {
}

func (pie *Pie) Render(w io.Writer) error {
	slices.SortFunc(pie.series, func(a, b opts.PieData) int {
		return strings.Compare(a.Name, b.Name)
	})
	commonPrefixLen := -1
	for i := 0; i < len(pie.series[0].Name) && i < len(pie.series[len(pie.series)-1].Name); i++ {
		if pie.series[0].Name[i] != pie.series[len(pie.series)-1].Name[i] {
			break
		}
		commonPrefixLen = i
	}
	for i := range pie.series {
		pie.series[i].Name = pie.series[i].Name[commonPrefixLen+1:]
	}

	pie.AddSeries("", pie.series)
	pie.SetGlobalOptions(charts.WithLegendOpts(opts.Legend{
		Show: false,
	}))
	return pie.Pie.Render(w)
}

func (pie *Pie) AddY(name string, v []int) {
	for _, v := range v {
		pie.series = append(pie.series, opts.PieData{
			Name:  fmt.Sprintf("%s ($%d)", name, v),
			Value: v,
		})
	}
}

func (pie *Pie) Overlap(other Chart) {
	panic("nope")
}
