package mqe

import (
	"fmt"
	"sort"
	"strconv"
)

type GetYearIndexReq struct {
	Since          int64   `json:"since"`          // из фильтр-формы
	Until          int64   `json:"until"`          // из фильтр-формы
	LastDayOfMonth int     `json:"lastDayOfMonth"` // например, конец месяца 25 число
	FirstHourOfDay int     `json:"firstHourOfDay"` // например день начинается в 6:00
	MetricIDs      []int64 `json:"metricIDs"`
}

func (s *MeasureQueryEngine) GetYearIndex(req GetYearIndexReq) (years []int, err error) {
	if len(req.MetricIDs) == 0 {
		return
	}

	var (
		lte, gte string

		args = []any{
			req.MetricIDs,
		}
	)

	if req.Since > 0 {
		lte = " AND tm >= ?"
		args = append(args, req.Since)
	}

	if req.Until > 0 {
		gte = " AND tm <= ?"
		args = append(args, req.Until)
	}

	q := fmt.Sprintf(`
	SELECT YEAR(FROM_UNIXTIME(tm)) as y
	FROM f64
	WHERE metricID IN (?)%s%s
	GROUP BY y`,
		lte, gte)

	// fmt.Println(q)
	// fmt.Printf("%v\n", args)

	err = s.db.ListQuery(&years, q, args...)
	if err != nil {
		return
	}

	sort.Ints(years)
	return
}

type GetMonthIndexReq struct {
	Since          int64   `json:"since"`          // из фильтр-формы
	Until          int64   `json:"until"`          // из фильтр-формы
	LastDayOfMonth int     `json:"lastDayOfMonth"` // например, конец месяца 25 число
	FirstHourOfDay int     `json:"firstHourOfDay"` // например день начинается в 6:00
	MetricIDs      []int64 `json:"metricIDs"`
}

type YearMonth struct {
	Year   int   `json:"year"`
	Months []int `json:"months"`
}

func (s *MeasureQueryEngine) GetMonthIndex(req GetMonthIndexReq) (years []YearMonth, err error) {
	if len(req.MetricIDs) == 0 {
		return
	}

	dt, err := getDateTimeFacingFHDAndLDM(req.FirstHourOfDay, req.LastDayOfMonth)
	if err != nil {
		return
	}

	var (
		lte, gte string

		args = []any{
			req.MetricIDs,
		}
	)

	if req.Since > 0 {
		lte = " AND tm >= ?"
		args = append(args, req.Since)
	}

	if req.Until > 0 {
		gte = " AND tm <= ?"
		args = append(args, req.Until)
	}

	// ВАЖНО!
	// Не сортируем средствами MySQL чтобы не создавать временный файл
	query := fmt.Sprintf(`
	SELECT DATE_FORMAT(%s, '%s') as p
	FROM f64
	WHERE metricID IN (?)%s%s
	GROUP BY p`,
		dt, "%Y%m", lte, gte)

	var (
		temp []string
		yms  []int
	)

	err = s.db.ListQuery(&temp, query, args...)
	if err != nil {
		return
	}

	for _, str := range temp {
		var ym int
		ym, err = strconv.Atoi(str)
		if err != nil {
			return
		}
		yms = append(yms, ym)
	}
	return IndexYms(yms), nil
}

type GetDayIndexReq struct {
	Since          int64   `json:"since"`          // из фильтр-формы
	Until          int64   `json:"until"`          // из фильтр-формы
	FirstHourOfDay int     `json:"firstHourOfDay"` // например день начинается в 6:00
	MetricIDs      []int64 `json:"metricIDs"`
}

type YearMonthDay struct {
	Year   int        `json:"year"`
	Months []MonthDay `json:"months"`
}

type MonthDay struct {
	Month int   `json:"month"`
	Days  []int `json:"days"`
}

func (s *MeasureQueryEngine) GetDayIndex(req GetDayIndexReq) (years []YearMonthDay, err error) {
	if len(req.MetricIDs) == 0 {
		return
	}

	dt, err := getDateTimeFacingFHD(req.FirstHourOfDay)
	if err != nil {
		return
	}

	var (
		lte, gte string

		args = []any{
			req.MetricIDs,
		}
	)

	if req.Since > 0 {
		lte = " AND tm >= ?"
		args = append(args, req.Since)
	}

	if req.Until > 0 {
		gte = " AND tm <= ?"
		args = append(args, req.Until)
	}
	// ВАЖНО!
	// Не сортируем средствами MySQL чтобы не создавать временный файл
	query := fmt.Sprintf(`
	SELECT DATE_FORMAT(%s, '%s') as p
	FROM f64
	WHERE metricID IN(?)%s%s
	GROUP BY p`,
		dt, "%Y%m%d", lte, gte)

	//fmt.Printf("%s\n", query)

	var (
		temp []string
		ymds []int
	)

	err = s.db.ListQuery(&temp, query, args...)
	if err != nil {
		return
	}

	//pretty.PPrintln("temp", temp)

	for _, str := range temp {
		var ymd int
		ymd, err = strconv.Atoi(str)
		if err != nil {
			return
		}
		ymds = append(ymds, ymd)
	}

	return IndexYmds(ymds), nil
}

func IndexYmds(ymds []int) (years []YearMonthDay) {
	if len(ymds) == 0 {
		return
	}

	sort.Ints(ymds)

	var (
		months           []MonthDay
		days             []int
		year, month, day int
	)

	for _, ymd := range ymds {
		y := ymd / 10000
		m := (ymd % 10000) / 100
		d := ymd % 100

		if year == 0 {
			// инициализация
			year = y
			month = m
			day = d
		} else {
			if y != year {
				days = append(days, day)
				months = append(months, MonthDay{
					Month: month,
					Days:  days,
				})
				years = append(years, YearMonthDay{
					Year:   year,
					Months: months,
				})
				year = y
				month = m
				day = d
				months = nil
				days = nil
			} else if m != month {
				days = append(days, day)
				months = append(months, MonthDay{
					Month: month,
					Days:  days,
				})
				month = m
				day = d
				days = nil
			} else if d != day {
				days = append(days, day)
				day = d
			}
		}
	}

	days = append(days, day)
	months = append(months, MonthDay{
		Month: month,
		Days:  days,
	})
	years = append(years, YearMonthDay{
		Year:   year,
		Months: months,
	})
	return
}

func IndexYms(yms []int) (years []YearMonth) {
	if len(yms) == 0 {
		return
	}

	sort.Ints(yms)

	var (
		months      []int
		year, month int
	)

	for _, ym := range yms {
		y := ym / 100
		m := ym % 100

		if year == 0 {
			// инициализация
			year = y
			month = m
		} else {
			if y != year {
				months = append(months, month)
				years = append(years, YearMonth{
					Year:   year,
					Months: months,
				})
				year = y
				month = m
				months = nil
			} else if m != month {
				months = append(months, month)
				month = m
			}
		}
	}
	months = append(months, month)
	years = append(years, YearMonth{
		Year:   year,
		Months: months,
	})
	return
}

//SELECT DATE_FORMAT(FROM_UNIXTIME(tm), '%Y%m%d') as p FROM f64 WHERE metricID IN (50) AND tm >= 1700863200 AND tm <= 1700949599 GROUP BY p;
