package autohost_grafana

import (
	"fmt"
	"strings"
)

const (
	ColorP50             = "dark-purple"
	ColorP90             = "dark-blue"
	ColorP99             = "super-light-blue"
	ColorSuccessRate     = "rgb(131, 161, 122)"
	ColorClientErrorRate = "semi-dark-yellow"
	ColorServerErrorRate = "semi-dark-red"
)

type OperationPanelArgs struct {
	DataSource string
	ID         int
	Namespace  string
	Operation  string
	Region     string
	Service    string
	Stage      string
	Substage   string
	W          int
	H          int
	X          int
	Y          int
}

func NewOperationLatencyPanel(args OperationPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	p.AliasColors = map[string]string{
		"p50": ColorP50,
		"p90": ColorP90,
		"p99": ColorP99,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = fmt.Sprintf("%s Latency", args.Operation)
	p.Targets = []GrafanaTarget{
		{
			Alias: "{{stat}}",
			Dimensions: map[string]string{
				"Operation": args.Operation,
				"Region":    args.Region,
				"Service":   args.Service,
				"Stage":     args.Stage,
				"Substage":  args.Substage,
			},
			Expression: "",
			Hide:       false,
			ID:         "m1",
			MatchExact: true,
			MetricName: "Duration",
			Namespace:  args.Namespace,
			RefID:      "A",
			Region:     args.Region,
			Statistics: []string{
				"p99",
				"p90",
				"p50",
			},
		},
	}

	return p
}

func NewOperationThroughputPanel(args OperationPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	dimensions := map[string]string{
		"Operation": args.Operation,
		"Region":    args.Region,
		"Service":   args.Service,
		"Stage":     args.Stage,
		"Substage":  args.Substage,
	}
	successSumTarget := GrafanaTarget{
		Alias:      "Total Successes",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "success_sum",
		MatchExact: true,
		MetricName: "Success",
		Namespace:  args.Namespace,
		RefID:      "A",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	clientErrorSumTarget := GrafanaTarget{
		Alias:      "Total Client Errors",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "client_error_sum",
		MatchExact: true,
		MetricName: "ClientError",
		Namespace:  args.Namespace,
		RefID:      "B",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	serverErrorSumTarget := GrafanaTarget{
		Alias:      "Total Server Errors",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "server_error_sum",
		MatchExact: true,
		MetricName: "ServerError",
		Namespace:  args.Namespace,
		RefID:      "C",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	successRateTarget := GrafanaTarget{
		Alias:      "Success Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(successSumTarget.ID),
		ID:         "success_rate",
		MatchExact: true,
		RefID:      "D",
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}
	clientErrorRateTarget := GrafanaTarget{
		Alias:      "Client Error Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(clientErrorSumTarget.ID),
		ID:         "client_error_rate",
		MatchExact: true,
		RefID:      "E",
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}
	serverErrorRateTarget := GrafanaTarget{
		Alias:      "Server Error Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(serverErrorSumTarget.ID),
		ID:         "server_error_rate",
		MatchExact: true,
		RefID:      "F",
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}

	p.AliasColors = map[string]string{
		clientErrorRateTarget.Alias: ColorClientErrorRate,
		serverErrorRateTarget.Alias: ColorServerErrorRate,
		successRateTarget.Alias:     ColorSuccessRate,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = fmt.Sprintf("%s Throughput", args.Operation)
	p.Targets = []GrafanaTarget{
		successSumTarget,
		clientErrorSumTarget,
		serverErrorSumTarget,
		successRateTarget,
		clientErrorRateTarget,
		serverErrorRateTarget,
	}
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "reqps",
		LogBase: 1,
		Show:    true,
	}

	return p
}

func NewOperationErrorSummaryPanel(args OperationPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	dimensions := map[string]string{
		"Operation": args.Operation,
		"Region":    args.Region,
		"Service":   args.Service,
		"Stage":     args.Stage,
		"Substage":  args.Substage,
	}
	successSumTarget := GrafanaTarget{
		Alias:      "Successes",
		Dimensions: dimensions,
		ID:         "success_sum",
		MatchExact: true,
		MetricName: "Success",
		Namespace:  args.Namespace,
		RefID:      "A",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	clientErrorSumTarget := GrafanaTarget{
		Alias:      "Client Errors",
		Dimensions: dimensions,
		ID:         "client_error_sum",
		MatchExact: true,
		MetricName: "ClientError",
		Namespace:  args.Namespace,
		RefID:      "B",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	serverErrorSumTarget := GrafanaTarget{
		Alias:      "Server Errors",
		Dimensions: dimensions,
		ID:         "server_error_sum",
		MatchExact: true,
		MetricName: "ServerError",
		Namespace:  args.Namespace,
		RefID:      "C",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}

	p.AliasColors = map[string]string{
		clientErrorSumTarget.Alias: ColorClientErrorRate,
		serverErrorSumTarget.Alias: ColorServerErrorRate,
		successSumTarget.Alias:     ColorSuccessRate,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Operation
	p.Targets = []GrafanaTarget{
		successSumTarget,
		clientErrorSumTarget,
		serverErrorSumTarget,
	}
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "short",
		LogBase: 1,
		Show:    true,
		Min:     StrPtr("85"),
		Max:     StrPtr("100"),
	}
	p.Percentage = true
	p.Stack = true
	p.Legend = GrafanaLegend{
		Show: true,
	}
	p.Lines = false
	p.Bars = true

	return p
}

func NewOperationLatencySummaryPanel(args OperationPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	p.AliasColors = map[string]string{
		"p90": ColorP90,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Operation
	p.Targets = []GrafanaTarget{
		{
			Alias: "{{stat}}",
			Dimensions: map[string]string{
				"Operation": args.Operation,
				"Region":    args.Region,
				"Service":   args.Service,
				"Stage":     args.Stage,
				"Substage":  args.Substage,
			},
			Expression: "",
			Hide:       false,
			ID:         "m1",
			MatchExact: true,
			MetricName: "Duration",
			Namespace:  args.Namespace,
			RefID:      "A",
			Region:     args.Region,
			Statistics: []string{
				"p90",
			},
		},
	}

	return p
}

func NewOperationThroughputSummaryPanel(args OperationPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	dimensions := map[string]string{
		"Operation": args.Operation,
		"Region":    args.Region,
		"Service":   args.Service,
		"Stage":     args.Stage,
		"Substage":  args.Substage,
	}
	requestCountTarget := GrafanaTarget{
		Alias:      "Total Requests",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "request_count",
		MatchExact: true,
		MetricName: "Duration",
		Namespace:  args.Namespace,
		RefID:      "A",
		Region:     args.Region,
		Statistics: []string{StatSampleCount},
	}
	requestRateTarget := GrafanaTarget{
		Alias:      "Request Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(requestCountTarget.ID),
		ID:         "request_rate",
		MatchExact: true,
		RefID:      "B",
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}

	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Operation
	p.Targets = []GrafanaTarget{
		requestCountTarget,
		requestRateTarget,
	}
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "reqps",
		LogBase: 1,
		Show:    true,
	}

	return p
}

type DependencyPanelArgs struct {
	DataSource string
	Dependency string
	ID         int
	Namespace  string
	Operation  string // Leave blank to create a rollup panel.
	Region     string
	Service    string
	Stage      string
	Substage   string
	Title      string
	W          int
	H          int
	X          int
	Y          int
}

func NewDependencyLatencyPanel(args DependencyPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()
	p.AliasColors = map[string]string{
		"p50": ColorP50,
		"p90": ColorP90,
		"p99": ColorP99,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Title

	dimensions := map[string]string{
		"Dependency": args.Dependency,
		"Region":     args.Region,
		"Service":    args.Service,
		"Stage":      args.Stage,
		"Substage":   args.Substage,
	}
	if args.Operation != "" {
		dimensions["Operation"] = args.Operation
	}

	p.Targets = []GrafanaTarget{
		{
			Alias:      "{{stat}}",
			Dimensions: dimensions,
			Expression: "",
			Hide:       false,
			ID:         "",
			MatchExact: true,
			MetricName: "DependencyDuration",
			Namespace:  args.Namespace,
			RefID:      "A",
			Region:     args.Region,
			Statistics: []string{
				"p99",
				"p90",
				"p50",
			},
		},
	}

	return p
}

type RefIDs struct {
	next rune
}

func (r *RefIDs) GetNext() string {
	if r.next > 'Z' {
		panic("No more letters")
	}
	if r.next == 0 {
		r.next = 'A'
	}

	current := r.next
	r.next++

	return fmt.Sprintf("%c", current)
}

func NewDependencyThroughputPanel(args DependencyPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	dimensions := map[string]string{
		"Dependency": args.Dependency,
		"Region":     args.Region,
		"Service":    args.Service,
		"Stage":      args.Stage,
		"Substage":   args.Substage,
	}
	if args.Operation != "" {
		dimensions["Operation"] = args.Operation
	}

	refIDs := RefIDs{}
	successSumTarget := GrafanaTarget{
		Alias:      "Total Successes",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "success_sum",
		MatchExact: true,
		MetricName: "DependencySuccess",
		Namespace:  args.Namespace,
		RefID:      refIDs.GetNext(),
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	clientErrorSumTarget := GrafanaTarget{
		Alias:      "Total Client Errors",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "client_error_sum",
		MatchExact: true,
		MetricName: "DependencyClientError",
		Namespace:  args.Namespace,
		RefID:      refIDs.GetNext(),
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	serverErrorSumTarget := GrafanaTarget{
		Alias:      "Total Server Errors",
		Dimensions: dimensions,
		Hide:       true,
		ID:         "server_error_sum",
		MatchExact: true,
		MetricName: "DependencyServerError",
		Namespace:  args.Namespace,
		RefID:      refIDs.GetNext(),
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	successRateTarget := GrafanaTarget{
		Alias:      "Success Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(successSumTarget.ID),
		ID:         "success_rate",
		MatchExact: true,
		RefID:      refIDs.GetNext(),
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}
	clientErrorRateTarget := GrafanaTarget{
		Alias:      "Client Error Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(clientErrorSumTarget.ID),
		ID:         "client_error_rate",
		MatchExact: true,
		RefID:      refIDs.GetNext(),
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}
	serverErrorRateTarget := GrafanaTarget{
		Alias:      "Server Error Rate",
		Dimensions: EmptyDimensions,
		Expression: RateExpression(serverErrorSumTarget.ID),
		ID:         "server_error_rate",
		MatchExact: true,
		RefID:      refIDs.GetNext(),
		Region:     RegionDefault,
		Statistics: []string{StatAverage},
	}

	p.AliasColors = map[string]string{
		clientErrorRateTarget.Alias: ColorClientErrorRate,
		serverErrorRateTarget.Alias: ColorServerErrorRate,
		successRateTarget.Alias:     ColorSuccessRate,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Title
	p.Targets = []GrafanaTarget{
		successSumTarget,
		clientErrorSumTarget,
		serverErrorSumTarget,
		successRateTarget,
		clientErrorRateTarget,
		serverErrorRateTarget,
	}
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "reqps",
		LogBase: 1,
		Show:    true,
	}

	return p
}

func NewDependencyTotalPanel(args DependencyPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	dimensions := map[string]string{
		"Dependency": args.Dependency,
		"Operation":  args.Operation,
		"Region":     args.Region,
		"Service":    args.Service,
		"Stage":      args.Stage,
		"Substage":   args.Substage,
	}
	successSumTarget := GrafanaTarget{
		Alias:      "Total Successes",
		Dimensions: dimensions,
		ID:         "success_sum",
		MatchExact: true,
		MetricName: "DependencySuccess",
		Namespace:  args.Namespace,
		RefID:      "A",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	clientErrorSumTarget := GrafanaTarget{
		Alias:      "Total Client Errors",
		Dimensions: dimensions,
		ID:         "client_error_sum",
		MatchExact: true,
		MetricName: "DependencyClientError",
		Namespace:  args.Namespace,
		RefID:      "B",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}
	serverErrorSumTarget := GrafanaTarget{
		Alias:      "Total Server Errors",
		Dimensions: dimensions,
		ID:         "server_error_sum",
		MatchExact: true,
		MetricName: "DependencyServerError",
		Namespace:  args.Namespace,
		RefID:      "C",
		Region:     args.Region,
		Statistics: []string{StatSum},
	}

	p.AliasColors = map[string]string{
		clientErrorSumTarget.Alias: ColorClientErrorRate,
		serverErrorSumTarget.Alias: ColorServerErrorRate,
		successSumTarget.Alias:     ColorSuccessRate,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Title
	p.Targets = []GrafanaTarget{
		successSumTarget,
		clientErrorSumTarget,
		serverErrorSumTarget,
	}
	p.Bars = true
	p.Stack = true
	p.Lines = false
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "none",
		LogBase: 1,
		Show:    true,
	}

	return p
}

type CircuitOpenPanelArgs struct {
	DataSource  string
	CircuitName string
	ID          int
	Namespace   string
	Region      string
	Service     string
	Stage       string
	Substage    string
	Title       string
	W           int
	H           int
	X           int
	Y           int
}

func NewCircuitOpenPanel(args CircuitOpenPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	p.AliasColors = map[string]string{
		"Total open": ColorServerErrorRate,
	}
	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Title
	p.Targets = []GrafanaTarget{
		{
			Alias: "Total open",
			Dimensions: map[string]string{
				"CircuitName": args.CircuitName,
				"Producer":    "circuit",
				"Region":      args.Region,
				"Service":     args.Service,
				"Stage":       args.Stage,
				"Substage":    args.Substage,
			},
			Hide:       false,
			ID:         "",
			MatchExact: true,
			MetricName: "IsOpen",
			Namespace:  args.Namespace,
			RefID:      "A",
			Region:     args.Region,
			Statistics: []string{
				"Sum",
			},
		},
	}
	p.Bars = true
	p.Stack = true
	p.Lines = false
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "none",
		LogBase: 1,
		Show:    true,
		Min:     StrPtr("0"),
	}
	p.Legend = GrafanaLegend{
		Show: true,
	}

	return p
}

type CircuitThroughputPanelArgs struct {
	DataSource  string
	CircuitName string
	ID          int
	Namespace   string
	Region      string
	Service     string
	Stage       string
	Substage    string
	Title       string
	W           int
	H           int
	X           int
	Y           int
}

type ThroughputTargetArgs struct {
	AliasPrefix string
	MetricName  string
}

func NewCircuitThroughputPanel(args CircuitThroughputPanelArgs) GrafanaGraphPanel {
	p := NewGrafanaGraphPanel()

	dimensions := map[string]string{
		"CircuitName": args.CircuitName,
		"Producer":    "circuit.run",
		"Region":      args.Region,
		"Service":     args.Service,
		"Stage":       args.Stage,
		"Substage":    args.Substage,
	}

	refIDs := RefIDs{}

	targetArgs := []ThroughputTargetArgs{
		{
			AliasPrefix: "Success",
			MetricName:  "Success",
		},
		{
			AliasPrefix: "BadRequest Error",
			MetricName:  "Error_BadRequest",
		},
		{
			AliasPrefix: "Server Error",
			MetricName:  "Error_Failure",
		},
		{
			AliasPrefix: "Timeout Error",
			MetricName:  "Error_Timeout",
		},
		{
			AliasPrefix: "Parent Cancelled Error",
			MetricName:  "Error_Interrupt",
		},
		{
			AliasPrefix: "ShotCircuit Error",
			MetricName:  "Error_ShortCircuit",
		},
	}

	targets := make([]GrafanaTarget, 0)
	for _, tArgs := range targetArgs {
		sumTargetID := fmt.Sprintf("%s_sum", strings.ToLower(tArgs.MetricName))
		sumTarget := GrafanaTarget{
			Alias:      fmt.Sprintf("Total %s", tArgs.AliasPrefix),
			Dimensions: dimensions,
			Hide:       true,
			ID:         sumTargetID,
			MatchExact: true,
			MetricName: tArgs.MetricName,
			Namespace:  args.Namespace,
			RefID:      refIDs.GetNext(),
			Region:     args.Region,
			Statistics: []string{StatSum},
		}

		rateTarget := GrafanaTarget{
			Alias:      fmt.Sprintf("%s Rate", tArgs.AliasPrefix),
			Dimensions: EmptyDimensions,
			Expression: RateExpression(sumTargetID),
			ID:         fmt.Sprintf("%s_rate", strings.ToLower(tArgs.MetricName)),
			MatchExact: true,
			RefID:      refIDs.GetNext(),
			Region:     RegionDefault,
			Statistics: []string{StatAverage},
		}

		targets = append(targets, sumTarget)
		targets = append(targets, rateTarget)
	}

	/*
		p.AliasColors = map[string]string{
			clientErrorRateTarget.Alias: ColorClientErrorRate,
			serverErrorRateTarget.Alias: ColorServerErrorRate,
			successRateTarget.Alias:     ColorSuccessRate,
		}
	*/

	p.DataSource = StrPtr(args.DataSource)
	p.GridPos = GrafanaGridPos{
		H: args.H,
		W: args.W,
		X: args.X,
		Y: args.Y,
	}
	p.ID = args.ID
	p.Title = args.Title
	p.Targets = targets
	p.YAxes[0] = GrafanaYAxesItem{
		Format:  "reqps",
		LogBase: 1,
		Show:    true,
	}

	return p
}
