package grafana

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"code.justin.tv/dta/twitch-create-service/internal/grafana/go_http"
	"code.justin.tv/dta/twitch-create-service/internal/grafana/ruby_http"
	"code.justin.tv/dta/twitch-create-service/internal/grafana/structs"
)

func Int(i int) *int {
	return &i
}

func CreateDashboard(owner, name, appType, apiToken, awsAccount string) error {
	var app structs.AppType
	switch appType {
	case "go_http":
		app = &go_http.App{}
	case "ruby_http":
		app = &ruby_http.App{}
	default:
		return fmt.Errorf("unknown app type %v", appType)
	}

	d := structs.DashboardRequest{
		Dashboard: &structs.Dashboard{
			Title:         name,
			Tags:          []string{},
			Timezone:      "browser",
			Refresh:       "1m",
			SchemaVersion: 6,
			Templating: structs.Templates{
				List: []structs.TemplateVar{
					{
						Name:       "env",
						Query:      fmt.Sprintf("stats.counters.%v.*", name),
						Type:       "query",
						Refresh:    true,
						Datasource: "Graphite",
					},
				},
			},
			Time: structs.TimeWindow{
				From: "now-1h",
				To:   "now",
			},
			Annotations: &structs.Annotations{
				List: []structs.Annotation{
					{
						Datasource: "Graphite",
						Enable:     true,
						IconColor:  "#C0C6BE",
						IconSize:   13,
						LineColor:  "rgba(255, 96, 96, 0.592157)",
						Name:       fmt.Sprintf("%v deploy", name),
						ShowLine:   true,
						Target:     fmt.Sprintf(`alias(stats.counters.deploys.%v.%v.$env.count, "%v")`, owner, name, name),
					},
				},
			},
		},
		// Overwrite: true,
	}

	defaultRows := []*structs.Row{
		newThroughputRow(owner, name),
		newELBRow(name, awsAccount),
	}

	rows, err := app.NewRows(owner, name)
	if err != nil {
		return err
	}

	for _, r := range rows {
		defaultRows = append(defaultRows, r)
	}
	d.Dashboard.Rows = defaultRows

	j, err := json.Marshal(d)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", "https://grafana.internal.justin.tv/api/dashboards/db", bytes.NewBuffer(j))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

	auth := fmt.Sprintf("Bearer %s", apiToken)
	req.Header.Set("Authorization", auth)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		err := resp.Body.Close()
		if err != nil {
			log.Printf("error closing response body: %v", err)
		}
	}()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		return fmt.Errorf("error creating dashboard: %v - %v", resp.StatusCode, string(body))
	}

	log.Print("Monitoring dashboard ready")

	return nil
}

func newThroughputRow(owner, name string) *structs.Row {
	r := &structs.Row{
		Editable: true,
		Height:   "250px",
		Panels: []*structs.Panel{
			newThroughputPanel(owner, name),
		},
	}

	return r
}

func newThroughputPanel(owner, name string) *structs.Panel {
	p := &structs.Panel{
		Editable: true,
		Fill:     1,
		Grid: &structs.Grid{
			LeftLogBase:  1,
			RightLogBase: 1,
		},
		ID:             1,
		LeftYAxisLabel: "requests/sec",
		Legend: structs.Legend{
			Show: true,
		},
		Lines:           true,
		LineWidth:       2,
		NullPointMode:   "null as zero",
		PointRadius:     5,
		Renderer:        "flot",
		RightYAxisLabel: "of total",
		SeriesOverrides: []structs.Override{
			{
				Alias: "5xx",
				YAxis: 2,
			},
			{
				Alias: "4xx",
				YAxis: 2,
			},
		},
		Span: 12,
		Targets: []structs.Target{
			{
				RefID:  "A",
				Target: fmt.Sprintf("alias(movingAverage(sumSeries(stats.counters.trace.data.v1.code-justin-tv-%v-%v.xxx.xxx.*.*.rate), 5), 'all requests')", owner, name),
			},
			{
				Hide:   true,
				RefID:  "B",
				Target: fmt.Sprintf("alias(asPercent(movingAverage(sumSeries(stats.counters.trace.data.v1.code-justin-tv-%v-%v.xxx.xxx.*.4*.rate), 5), #A), '4xx')", owner, name),
			},
			{
				Hide:   true,
				RefID:  "C",
				Target: fmt.Sprintf("alias(asPercent(movingAverage(sumSeries(stats.counters.trace.data.v1.code-justin-tv-%v-%v.xxx.xxx.*.5*.rate), 5), #A), '5xx')", owner, name),
			},
		},
		Title: "throughput",
		Tooltip: structs.Tooltip{
			Shared:    true,
			ValueType: "cumulative",
		},
		Type:     "graph",
		XAxis:    true,
		YAxis:    true,
		YFormats: []string{"short", "short"},
	}

	return p
}

func newELBRow(name, awsAccount string) *structs.Row {
	r := &structs.Row{
		Editable: true,
		Height:   "250px",
		Panels: []*structs.Panel{
			newELBHostsPanel(8, name, awsAccount),
			newELBSurgePanel(9, name, awsAccount),
			newELBSpilloverPanel(10, name, awsAccount),
		},
	}

	return r
}

func newELBHostsPanel(id int, name, awsAccount string) *structs.Panel {
	return structs.NewPanel(id, 4, "ELB Healthy Hosts", "", "", false, []structs.Target{
		{
			Dimensions: &structs.Dimensions{
				LoadBalancerName: fmt.Sprintf("%v-$env", name),
			},
			MetricName: "HealthyHostCount",
			Namespace:  "AWS/ELB",
			RefID:      "A",
			Region:     "us-west-2",
			Statistics: []string{"Average"},
		},
	}, fmt.Sprintf("CloudWatch (%v)", awsAccount))
}

func newELBSurgePanel(id int, name, awsAccount string) *structs.Panel {
	return structs.NewPanel(id, 4, "ELB Surge Queue Length", "", "", false, []structs.Target{
		{
			Dimensions: &structs.Dimensions{
				LoadBalancerName: fmt.Sprintf("%v-$env", name),
			},
			MetricName: "SurgeQueueLength",
			Namespace:  "AWS/ELB",
			RefID:      "A",
			Region:     "us-west-2",
			Statistics: []string{"Maximum"},
		},
	}, fmt.Sprintf("CloudWatch (%v)", awsAccount))
}

func newELBSpilloverPanel(id int, name, awsAccount string) *structs.Panel {
	return structs.NewPanel(id, 4, "ELB Spillover Count", "", "", false, []structs.Target{
		{
			Dimensions: &structs.Dimensions{
				LoadBalancerName: fmt.Sprintf("%v-$env", name),
			},
			MetricName: "SpilloverCount",
			Namespace:  "AWS/ELB",
			RefID:      "A",
			Region:     "us-west-2",
			Statistics: []string{"Sum"},
		},
	}, fmt.Sprintf("CloudWatch (%v)", awsAccount))
}
