package main

import (
	"fmt"
	"log"
	"time"

	diploma "gordenko.dev/dima/qb"
	"gordenko.dev/dima/qb/client"
	"gordenko.dev/dima/qb/proto"
)

func sendRequests(conn *client.Connection) {
	var (
		instantMetricID    uint32 = 10000
		cumulativeMetricID uint32 = 10001
		fracDigits         int    = 2
		err                error
	)

	conn.DeleteMetric(instantMetricID)
	conn.DeleteMetric(cumulativeMetricID)

	// ADD INSTANT METRIC

	err = conn.AddMetric(proto.AddMetricReq{
		MetricID:   instantMetricID,
		MetricType: diploma.Instant,
		FracDigits: fracDigits,
	})
	if err != nil {
		log.Fatalf("conn.AddMetric: %s\n", err)
	} else {
		fmt.Printf("\nInstant metric %d added\n", instantMetricID)
	}

	// GET INSTANT METRIC

	iMetric, err := conn.GetMetric(instantMetricID)
	if err != nil {
		log.Fatalf("conn.GetMetric: %s\n", err)
	} else {
		fmt.Printf(`
GetMetric:
  metricID: %d
  metricType: %s
  fracDigits: %d
`,
			iMetric.MetricID, metricTypeToName[iMetric.MetricType], fracDigits)
	}

	// APPEND MEASURES

	instantMeasures := GenerateInstantMeasures(62, 220)

	err = conn.AppendMeasures(proto.AppendMeasuresReq{
		MetricID: instantMetricID,
		Measures: instantMeasures,
	})
	if err != nil {
		log.Fatalf("conn.AppendMeasures: %s\n", err)
	} else {
		fmt.Printf("\nAppended %d measures for the metric %d\n",
			len(instantMeasures), instantMetricID)
	}

	// LIST INSTANT MEASURES

	lastTimestamp := instantMeasures[len(instantMeasures)-1].Timestamp
	until := time.Unix(int64(lastTimestamp), 0)
	since := until.Add(-5 * time.Hour)

	instantList, err := conn.ListInstantMeasures(proto.ListInstantMeasuresReq{
		MetricID: instantMetricID,
		Since:    uint32(since.Unix()),
		Until:    uint32(until.Unix()),
	})
	if err != nil {
		log.Fatalf("conn.ListInstantMeasures: %s\n", err)
	} else {
		fmt.Printf("\nListInstantMeasures %s - %s:\n",
			formatTime(uint32(since.Unix())), formatTime(uint32(until.Unix())))
		for _, item := range instantList {
			fmt.Printf("  %s => %.2f\n", formatTime(item.Timestamp), item.Value)
		}
	}

	// LIST ALL INSTANT MEASURES

	instantList, err = conn.ListAllInstantMeasures(instantMetricID)
	if err != nil {
		log.Fatalf("conn.ListAllInstantMeasures: %s\n", err)
	} else {
		fmt.Printf("\nListAllInstantMeasures (last 15 items):\n")
		for _, item := range instantList[:15] {
			fmt.Printf("  %s => %.2f\n", formatTime(item.Timestamp), item.Value)
		}
	}

	// LIST INSTANT PERIODS (group by hour)

	until = time.Unix(int64(lastTimestamp+1), 0)
	since = until.Add(-24 * time.Hour)

	instantPeriods, err := conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
		MetricID: instantMetricID,
		Since: proto.TimeBound{
			Year:  since.Year(),
			Month: since.Month(),
			Day:   since.Day(),
		},
		Until: proto.TimeBound{
			Year:  until.Year(),
			Month: until.Month(),
			Day:   until.Day(),
		},
		GroupBy:        diploma.GroupByHour,
		AggregateFuncs: diploma.AggregateMin | diploma.AggregateMax | diploma.AggregateAvg,
	})
	if err != nil {
		log.Fatalf("conn.ListInstantPeriods: %s\n", err)
	} else {
		fmt.Printf("\nListInstantPeriods (1 day, group by hour):\n")
		for _, item := range instantPeriods {
			fmt.Printf("  %s => min %.2f, max %.2f, avg %.2f\n", formatHourPeriod(item.Period), item.Min, item.Max, item.Avg)
		}
	}

	// LIST INSTANT PERIODS (group by day)

	until = time.Unix(int64(lastTimestamp+1), 0)
	since = until.AddDate(0, 0, -7)

	instantPeriods, err = conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
		MetricID: instantMetricID,
		Since: proto.TimeBound{
			Year:  since.Year(),
			Month: since.Month(),
			Day:   since.Day(),
		},
		Until: proto.TimeBound{
			Year:  until.Year(),
			Month: until.Month(),
			Day:   until.Day(),
		},
		GroupBy:        diploma.GroupByDay,
		AggregateFuncs: diploma.AggregateMin | diploma.AggregateMax | diploma.AggregateAvg,
	})
	if err != nil {
		log.Fatalf("conn.ListInstantPeriods: %s\n", err)
	} else {
		fmt.Printf("\nListInstantPeriods (7 days, group by day):\n")
		for _, item := range instantPeriods {
			fmt.Printf("  %s => min %.2f, max %.2f, avg %.2f\n", formatDayPeriod(item.Period), item.Min, item.Max, item.Avg)
		}
	}

	// LIST INSTANT PERIODS (group by month)

	until = time.Unix(int64(lastTimestamp+1), 0)
	since = until.AddDate(0, 0, -62)

	instantPeriods, err = conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
		MetricID: instantMetricID,
		Since: proto.TimeBound{
			Year:  since.Year(),
			Month: since.Month(),
			Day:   since.Day(),
		},
		Until: proto.TimeBound{
			Year:  until.Year(),
			Month: until.Month(),
			Day:   until.Day(),
		},
		GroupBy:        diploma.GroupByMonth,
		AggregateFuncs: diploma.AggregateMin | diploma.AggregateMax | diploma.AggregateAvg,
	})
	if err != nil {
		log.Fatalf("conn.ListInstantPeriods: %s\n", err)
	} else {
		fmt.Printf("\nListInstantPeriods (62 days, group by month):\n")
		for _, item := range instantPeriods {
			fmt.Printf("  %s => min %.2f, max %.2f, avg %.2f\n", formatMonthPeriod(item.Period), item.Min, item.Max, item.Avg)
		}
	}

	// DELETE INSTANT METRIC MEASURES

	err = conn.DeleteMeasures(proto.DeleteMeasuresReq{
		MetricID: instantMetricID,
	})
	if err != nil {
		log.Fatalf("conn.DeleteMeasures: %s\n", err)
	} else {
		fmt.Printf("\nInstant metric %d measures deleted\n", instantMetricID)
	}

	// DELETE INSTANT METRIC

	err = conn.DeleteMetric(instantMetricID)
	if err != nil {
		log.Fatalf("conn.DeleteMetric: %s\n", err)
	} else {
		fmt.Printf("\nInstant metric %d deleted\n", instantMetricID)
	}

	// ADD CUMULATIVE METRIC

	err = conn.AddMetric(proto.AddMetricReq{
		MetricID:   cumulativeMetricID,
		MetricType: diploma.Cumulative,
		FracDigits: fracDigits,
	})
	if err != nil {
		log.Fatalf("conn.AddMetric: %s\n", err)
	} else {
		fmt.Printf("\nCumulative metric %d added\n", cumulativeMetricID)
	}

	// GET CUMULATIVE METRIC

	cMetric, err := conn.GetMetric(cumulativeMetricID)
	if err != nil {
		log.Fatalf("conn.GetMetric: %s\n", err)
	} else {
		fmt.Printf(`
GetMetric:
  metricID: %d
  metricType: %s
  fracDigits: %d
`,
			cMetric.MetricID, metricTypeToName[cMetric.MetricType], fracDigits)
	}

	// APPEND MEASURES

	cumulativeMeasures := GenerateCumulativeMeasures(62)

	err = conn.AppendMeasures(proto.AppendMeasuresReq{
		MetricID: cumulativeMetricID,
		Measures: cumulativeMeasures,
	})
	if err != nil {
		log.Fatalf("conn.AppendMeasures: %s\n", err)
	} else {
		fmt.Printf("\nAppended %d measures for the metric %d\n",
			len(cumulativeMeasures), cumulativeMetricID)
	}

	// LIST CUMULATIVE MEASURES

	lastTimestamp = cumulativeMeasures[len(cumulativeMeasures)-1].Timestamp
	until = time.Unix(int64(lastTimestamp), 0)
	since = until.Add(-5 * time.Hour)

	cumulativeList, err := conn.ListCumulativeMeasures(proto.ListCumulativeMeasuresReq{
		MetricID: cumulativeMetricID,
		Since:    uint32(since.Unix()),
		Until:    uint32(until.Unix()),
	})
	if err != nil {
		log.Fatalf("conn.ListCumulativeMeasures: %s\n", err)
	} else {
		fmt.Printf("\nListCumulativeMeasures %s - %s:\n",
			formatTime(uint32(since.Unix())), formatTime(uint32(until.Unix())))

		for _, item := range cumulativeList {
			fmt.Printf("  %s => %.2f\n", formatTime(item.Timestamp), item.Value)
		}
	}

	// LIST ALL CUMULATIVE MEASURES

	cumulativeList, err = conn.ListAllCumulativeMeasures(cumulativeMetricID)
	if err != nil {
		log.Fatalf("conn.ListAllCumulativeMeasures: %s\n", err)
	} else {
		fmt.Printf("\nListAllCumulativeMeasures (last 15 items):\n")
		for _, item := range cumulativeList[:15] {
			fmt.Printf("  %s => %.2f\n", formatTime(item.Timestamp), item.Value)
		}
	}

	// LIST CUMULATIVE PERIODS (group by hour)

	until = time.Unix(int64(lastTimestamp+1), 0)
	since = until.Add(-24 * time.Hour)

	cumulativePeriods, err := conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
		MetricID: cumulativeMetricID,
		Since: proto.TimeBound{
			Year:  since.Year(),
			Month: since.Month(),
			Day:   since.Day(),
		},
		Until: proto.TimeBound{
			Year:  until.Year(),
			Month: until.Month(),
			Day:   until.Day(),
		},
		GroupBy: diploma.GroupByHour,
	})
	if err != nil {
		log.Fatalf("conn.ListCumulativePeriods: %s\n", err)
	} else {
		fmt.Printf("\nListCumulativePeriods (1 day, group by hour):\n")
		for _, item := range cumulativePeriods {
			fmt.Printf("  %s => end value %.2f, total %.2f\n", formatHourPeriod(item.Period), item.EndValue, item.Total)
		}
	}

	// LIST CUMULATIVE PERIODS (group by day)

	until = time.Unix(int64(lastTimestamp+1), 0)
	since = until.AddDate(0, 0, -7)

	cumulativePeriods, err = conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
		MetricID: cumulativeMetricID,
		Since: proto.TimeBound{
			Year:  since.Year(),
			Month: since.Month(),
			Day:   since.Day(),
		},
		Until: proto.TimeBound{
			Year:  until.Year(),
			Month: until.Month(),
			Day:   until.Day(),
		},
		GroupBy: diploma.GroupByDay,
	})
	if err != nil {
		log.Fatalf("conn.ListCumulativePeriods: %s\n", err)
	} else {
		fmt.Printf("\nListCumulativePeriods (7 days, group by day):\n")
		for _, item := range cumulativePeriods {
			fmt.Printf("  %s => end value %.2f, total %.2f\n", formatDayPeriod(item.Period), item.EndValue, item.Total)
		}
	}

	// LIST CUMULATIVE PERIODS (group by day)

	until = time.Unix(int64(lastTimestamp+1), 0)
	since = until.AddDate(0, 0, -62)

	cumulativePeriods, err = conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
		MetricID: cumulativeMetricID,
		Since: proto.TimeBound{
			Year:  since.Year(),
			Month: since.Month(),
			Day:   since.Day(),
		},
		Until: proto.TimeBound{
			Year:  until.Year(),
			Month: until.Month(),
			Day:   until.Day(),
		},
		GroupBy: diploma.GroupByMonth,
	})
	if err != nil {
		log.Fatalf("conn.ListCumulativePeriods: %s\n", err)
	} else {
		fmt.Printf("\nListCumulativePeriods (62 days, group by month):\n")
		for _, item := range cumulativePeriods {
			fmt.Printf("  %s => end value %.2f, total %.2f\n", formatMonthPeriod(item.Period), item.EndValue, item.Total)
		}
	}

	// DELETE CUMULATIVE METRIC MEASURES

	err = conn.DeleteMeasures(proto.DeleteMeasuresReq{
		MetricID: cumulativeMetricID,
	})
	if err != nil {
		log.Fatalf("conn.DeleteMeasures: %s\n", err)
	} else {
		fmt.Printf("\nCumulative metric %d measures deleted\n", cumulativeMetricID)
	}

	// DELETE CUMULATIVE METRIC

	err = conn.DeleteMetric(cumulativeMetricID)
	if err != nil {
		log.Fatalf("conn.DeleteMetric: %s\n", err)
	} else {
		fmt.Printf("\nCumulative metric %d deleted\n", cumulativeMetricID)
	}
}

const datetimeLayout = "2006-01-02 15:04:05"

func formatTime(timestamp uint32) string {
	tm := time.Unix(int64(timestamp), 0)
	return tm.Format(datetimeLayout)
}

func formatHourPeriod(period uint32) string {
	tm := time.Unix(int64(period), 0)
	return tm.Format("2006-01-02 15:00 - 15") + ":59"
}

func formatDayPeriod(period uint32) string {
	tm := time.Unix(int64(period), 0)
	return tm.Format("2006-01-02")
}

func formatMonthPeriod(period uint32) string {
	tm := time.Unix(int64(period), 0)
	return tm.Format("2006-01")
}
