package daemon

import (
	"fmt"
	"math"
	"strconv"
	"time"

	"gordenko.dev/dima/flashscore"
	fsmodel "gordenko.dev/dima/flashscore/model"
	"gordenko.dev/dima/onexbet"
	"gordenko.dev/dima/tipper/model"
)

var (
	p1TimeFrame = timeFrame{
		Since: 5 * 60,
		Until: 22 * 60,
	}
	ftTimeFrame = timeFrame{
		Since: 10 * 60,
		Until: 52 * 60,
	}

	ftOverTimeFrame = timeFrame{
		Since: 10 * 60,
		Until: 52 * 60,
	}
)

type timeFrame struct {
	Since int
	Until int
}

type HandballStrategyOptions struct {
	ID                          int64
	NotifyInTelegram            bool
	TotalDiff                   float64
	FixateGamePaceEveryNMinutes int
	Disable                     bool
}

// FIX - calculator, который будет проверять флаг isMatchScoreLoaded

func HandballHistoryProcessor(match *TeamMatch) {
	// Делаем reset
	match.HomeGoalsPerMinute = 0
	match.AwayGoalsPerMinute = 0
	match.H2HGoalsPerMinute = 0

	match.BasicTotal = 0
	/*
		match.BasicOverGoalsPerMinute = 0
		match.BasicUnderGoalsPerMinute = 0
		match.TipTotalOver = 0
		match.TipTotalUnder = 0
	*/

	// Считаем

	if len(match.HomeTeamMatches) > 0 {
		match.HomeGoalsPerMinute = caclulateGoalsPerMinuteOfLastNMatches(match.HomeTeamMatches, 5)
	}
	if len(match.AwayTeamMatches) > 0 {
		match.AwayGoalsPerMinute = caclulateGoalsPerMinuteOfLastNMatches(match.AwayTeamMatches, 5)
	}
	if len(match.H2H) > 0 {
		match.H2HGoalsPerMinute = caclulateGoalsPerMinuteOfLastNMatches(match.H2H, 5)
	}

	// Basic Totals

	match.BasicTotal = findBasicTotal(match.Odds.FullTimeTotal)
	match.H1BasicTotal = findBasicTotal(match.Odds.H1Total)
}

func findBasicTotal(totals []flashscore.Total) (_ float64) {
	var (
		minDif           float64
		bestOffer        flashscore.Total
		isBestOfferFound bool
	)

	// Находим средний тотал - это когда разница между коэффициентами минимальна
	for _, total := range totals {
		if len(total.Offers) > 0 {
			var dif float64
			for _, offer := range total.Offers {
				dif += math.Abs(offer.Over - offer.Under)
			}
			// Вычисляем для каждого тотала среднюю (среди всех букмекеров) разницу
			// между Over и Under коэф.
			dif = dif / float64(len(total.Offers))

			if (!isBestOfferFound) || (dif < minDif) {
				minDif = dif
				bestOffer = total
				isBestOfferFound = true
			}
		}
	}

	if isBestOfferFound {
		basicTotal, err := strconv.ParseFloat(bestOffer.Total, 64)
		if err != nil {
			return
		}

		return basicTotal
	}
	return
}

const (
	Gte = 1 // >=
	Lte = 2 // <=
)

func checkGamePace(basicGoalsPerMinute float64, match *TeamMatch, upd onexbet.TeamLiveMatchData, fixateGamePaceEveryNMinutes int, moreOrLess int) (comment string, isPassed bool) {
	currentGoalsPerMinute := (float64(upd.HomeGoals+upd.AwayGoals) / float64(upd.CurrentTime)) * 60
	gamePaceFrame := upd.CurrentTime / (fixateGamePaceEveryNMinutes * 60)

	if gamePaceFrame == 0 {
		match.GamePace = currentGoalsPerMinute
	} else {
		if gamePaceFrame != match.GamePaceFrame {
			match.GamePaceFrame = gamePaceFrame
			match.GamePace = currentGoalsPerMinute
		}
	}

	var timeStr string

	if match.GamePaceFrame == 0 {
		timeStr = fmt.Sprintf("%s", time.Duration(upd.CurrentTime)*time.Second)
	} else {
		timeStr = fmt.Sprintf("%d мин", match.GamePaceFrame*fixateGamePaceEveryNMinutes)
	}

	switch moreOrLess {
	case Gte:
		// Темп должен быть >=
		if match.GamePace < basicGoalsPerMinute {
			comment = fmt.Sprintf("Текущий темп %.2f (за %s) < базового темпа %.2f",
				match.GamePace, timeStr, basicGoalsPerMinute)
			return comment, false
		}

		/*
			if len(match.H2H) > 0 {
				// H2H матчи необязательныеs
				if match.GamePace < match.H2HGoalsPerMinute {
					comment = fmt.Sprintf("Текущий темп %.2f (за %s) < H2H темпа %.2f",
						match.GamePace, timeStr, match.H2HGoalsPerMinute)
					return comment, false
				}
			}
		*/

	case Lte:
		// Темп должен быть <=
		if match.GamePace > basicGoalsPerMinute {
			comment = fmt.Sprintf("Текущий темп %.2f (за %s) > базового темпа %.2f",
				match.GamePace, timeStr, basicGoalsPerMinute)
			return comment, false
		}

		/*
			if len(match.H2H) > 0 {
				// H2H матчи необязательныеs
				if match.GamePace > match.H2HGoalsPerMinute {
					comment = fmt.Sprintf("Текущий темп %.2f (за %s) > H2H темпа %.2f",
						match.GamePace, timeStr, match.H2HGoalsPerMinute)
					return comment, false
				}
			}
		*/

	default:
		panic(fmt.Sprintf("Bug: unknown moreOrLess: %d", moreOrLess))
	}

	return "", true
}

func correctTotal(total float64, roundDir int) float64 {
	_, frac := math.Modf(total)

	eps := 0.001

	if math.Abs(frac-0.25) < eps {
		// Если тотал завершается на 0.25 - округляем в ту или иную сторону
		switch roundDir {
		case roundUp:
			total = math.Floor(total) + 0.5

		case roundDown:
			total = math.Floor(total)
		}
	} else if frac < eps {
		// Круглый тотал: 24, 60
		switch roundDir {
		case roundUp:
			total = total + 0.5

		case roundDown:
			total = total - 0.5
		}
	}

	return total
}

type HandballFTOver struct {
	id                          int64
	notifyInTelegram            bool
	totalDiff                   float64
	fixateGamePaceEveryNMinutes int
	isDisabled                  bool
}

func NewHandballFTOver(opt HandballStrategyOptions) HandballFTOver {
	if opt.ID == 0 {
		panic("StrategyID not defined")
	}
	s := HandballFTOver{}
	s.id = opt.ID
	s.notifyInTelegram = opt.NotifyInTelegram
	s.totalDiff = opt.TotalDiff
	s.fixateGamePaceEveryNMinutes = opt.FixateGamePaceEveryNMinutes
	s.isDisabled = opt.Disable

	return s
}

func (s HandballFTOver) ID() int64 {
	return s.id
}

func (s HandballFTOver) ShortName() string {
	return fmt.Sprintf("#%d FT Over (-%v, %d min)", s.id, s.totalDiff, s.fixateGamePaceEveryNMinutes)
}

func (s HandballFTOver) IsNotifyInTelegram() bool {
	return s.notifyInTelegram
}

func (s HandballFTOver) GetTelegramTipPattern() string {
	return `Сигнал # %d.
Алгоритм %d. Тотал Больше в матче.
%s. %s
%s - %s

Точка входа %.1f кф. %.3f

Рекомендация: тотал больше %.1f и ниже (%.1f; %.1f)`
}

func (s HandballFTOver) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
	return true
}

func caclulateGoalsPerMinuteOfLastNMatches(matches []fsmodel.TeamMatch, n int) (goalsPerMinute float64) {
	if len(matches) < n {
		n = len(matches)
	}

	count := 0

	for _, m := range matches[:n] {
		if m.IsMatchScoreLoaded {
			goalsPerMinute += float64(m.HomeGoals+m.AwayGoals) / 60
			count++
		}
	}

	if count > 0 {
		return goalsPerMinute / float64(count)
	}
	return
}

func (s HandballFTOver) IsInteresting(match *TeamMatch) (notes interface{}, _ bool) {
	if s.isDisabled {
		return
	}
	eps := 0.01
	// Проверки на то, что полные данные

	if match.BasicTotal < 1 {
		return nil, false
	}

	tipTotal := match.BasicTotal - s.totalDiff

	// ВАЖНО!
	// После деления тотал может быть 25.25, либо просто круглый 24, 60
	tipTotal = correctTotal(tipTotal, roundDown)

	basicGoalsPerMinute := tipTotal / 60

	if math.Abs(match.H2HGoalsPerMinute) > eps {
		// Если команды между собой забивают меньше чем дает бук (с учетом погрешности)
		// - матч не интересен
		if match.H2HGoalsPerMinute < basicGoalsPerMinute {
			return
		}
	} else {
		if math.Abs(match.HomeGoalsPerMinute) < eps {
			return
		}

		if math.Abs(match.AwayGoalsPerMinute) < eps {
			return
		}
		// Считаем средний темп с которым играют обе команды
		avgGoalsPerMinute := (match.HomeGoalsPerMinute + match.AwayGoalsPerMinute) / 2

		// Если в среднем команды забивают меньше чем дает бук (с учетом погрешности)
		// - матч не интересен
		if avgGoalsPerMinute < basicGoalsPerMinute {
			return
		}
	}

	return nil, true
}

func (s HandballFTOver) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (comment string, _ *BetDetails, _ TipCode) {
	if upd.CurrentTime == 1800 {
		return "в перерыве не ставим (багфикс)", nil, Waiting
	}

	if upd.CurrentTime < ftOverTimeFrame.Since {
		return "еще рано", nil, Waiting
	}

	if upd.CurrentTime > ftOverTimeFrame.Until {
		return "время вышло", nil, Unwatch
	}

	tipTotal := match.BasicTotal - s.totalDiff

	// ВАЖНО!
	// После деления тотал может быть 25.25, либо просто круглый 24, 60
	tipTotal = correctTotal(tipTotal, roundDown)

	basicGoalsPerMinute := tipTotal / 60

	tipTotalStr := fmt.Sprintf("%.1f", tipTotal)

	if float64(upd.HomeGoals+upd.AwayGoals) > tipTotal {
		return "Тотал пробит", nil, Unwatch
	}

	// Текущий темп матча опережает рассчитанный на истории
	// проверяем коэффициенты.
	var isPassed bool

	comment, isPassed = checkGamePace(basicGoalsPerMinute, match, upd, s.fixateGamePaceEveryNMinutes, Gte)
	if !isPassed {
		return comment, nil, Waiting
	}

	// Задача - найти минимальный тотал <= tipTotal, чтобы коэфф. >= 1.5
	tipTotal = tipTotal + 0.01 // Для удобного сравнивания float

	//fmt.Printf("tipTotal: %.2f\n", tipTotal)

	var (
		minTotalValue float64
		bestOffer     onexbet.ParamOffer // строковое представление тотала
	)

	for _, offer := range upd.Total.Over {
		if offer.Price < 1.5 {
			continue
		}

		totalValue, err := strconv.ParseFloat(offer.Param, 64)
		if err != nil {
			continue
		}

		if totalValue > tipTotal {
			//fmt.Printf("totalValue > tipTotal: %.2f > %.2f\n", totalValue, tipTotal)
			continue
		}

		//fmt.Printf("Best: totalValue: %2.f (%s), price: %.2f\n", totalValue, offer.Param, offer.Price)

		if bestOffer.Param == "" {
			// инициализация
			minTotalValue = totalValue
			bestOffer = offer
		} else {
			if totalValue < minTotalValue {
				minTotalValue = totalValue
				bestOffer = offer
			}
		}
	}

	if bestOffer.Param == "" {
		comment = fmt.Sprintf("Тотал %s и меньше с коэф. >= 1.5 не найден",
			tipTotalStr)
		return comment, nil, Waiting
	}

	return "", &BetDetails{
		Market: MarketTotal,
		Side:   SideOver,
		Param:  bestOffer.Param,
		Price:  bestOffer.Price,
	}, Bet
}

func (s HandballFTOver) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) {
	//buf, _ := json.MarshalIndent(stats, "", "    ")
	//fmt.Printf("%s\n", buf)

	if stats.Status != onexbet.StatusMatchCompleted {
		return
	}

	tipTotal, err := strconv.ParseFloat(tip.Param, 64)
	if err != nil {
		return
	}

	res := &TipResult{
		Result: fmt.Sprintf("счет: %d-%d", stats.HomePoints, stats.AwayPoints),
	}

	goals := float64(stats.HomePoints + stats.AwayPoints)

	// Сперва проверяем что тоталы не равны
	dif := goals - tipTotal

	eps := 0.0001
	if math.Abs(dif) < eps {
		// Равны - расход
		res.Status = model.Void
	} else {
		if goals > tipTotal {
			res.Status = model.Won
		} else {
			res.Status = model.Lost
		}
	}
	return res
}

type HandballFTUnder struct {
	id                          int64
	notifyInTelegram            bool
	totalDiff                   float64
	fixateGamePaceEveryNMinutes int
	isDisabled                  bool
}

func NewHandballFTUnder(opt HandballStrategyOptions) HandballFTUnder {
	if opt.ID == 0 {
		panic("StrategyID not defined")
	}
	s := HandballFTUnder{}
	s.id = opt.ID
	s.notifyInTelegram = opt.NotifyInTelegram
	s.totalDiff = opt.TotalDiff
	s.fixateGamePaceEveryNMinutes = opt.FixateGamePaceEveryNMinutes
	s.isDisabled = opt.Disable

	return s
}

func (s HandballFTUnder) ID() int64 {
	return s.id
}

func (s HandballFTUnder) ShortName() string {
	return fmt.Sprintf("#%d FT Under (+%v, %d min)", s.id, s.totalDiff, s.fixateGamePaceEveryNMinutes)
}

func (s HandballFTUnder) IsNotifyInTelegram() bool {
	return s.notifyInTelegram
}

func (s HandballFTUnder) GetTelegramTipPattern() string {
	return `Сигнал # %d.
Алгоритм %d. Тотал Меньше в матче.
%s. %s
%s - %s

Точка входа %.1f кф. %.3f

Рекомендация: тотал меньше %.1f и выше (%.1f; %.1f)`
}

func (s HandballFTUnder) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
	return true
}

func (s HandballFTUnder) IsInteresting(match *TeamMatch) (notes interface{}, _ bool) {
	if s.isDisabled {
		return
	}

	eps := 0.01
	// Проверки на то, что полные данные

	if match.BasicTotal < 1 {
		return nil, false
	}

	tipTotal := match.BasicTotal + s.totalDiff

	// ВАЖНО!
	// После деления тотал может быть 25.25, либо просто круглый 24, 60
	tipTotal = correctTotal(tipTotal, roundUp)

	basicGoalsPerMinute := tipTotal / 60

	if math.Abs(match.H2HGoalsPerMinute) > eps {
		// Если команды между собой забивают больше чем дает бук (с учетом погрешности)
		// - матч не интересен (ибо опасно)
		if match.H2HGoalsPerMinute > basicGoalsPerMinute {
			return
		}
	} else {
		if math.Abs(match.HomeGoalsPerMinute) < eps {
			return
		}

		if math.Abs(match.AwayGoalsPerMinute) < eps {
			return
		}
		// Считаем средний темп с которым играют обе команды
		avgGoalsPerMinute := (match.HomeGoalsPerMinute + match.AwayGoalsPerMinute) / 2

		// Если в среднем команды забивают больше чем дает бук (с учетом погрешности)
		// - матч не интересен (ибо опасно)
		if avgGoalsPerMinute > basicGoalsPerMinute {
			return
		}
	}
	return nil, true
}

func (s HandballFTUnder) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (comment string, _ *BetDetails, _ TipCode) {
	if upd.CurrentTime == 1800 {
		return "в перерыве не ставим (багфикс)", nil, Waiting
	}

	if upd.CurrentTime < ftTimeFrame.Since {
		return "еще рано", nil, Waiting
	}

	if upd.CurrentTime > ftTimeFrame.Until {
		return "время вышло", nil, Unwatch
	}

	tipTotal := match.BasicTotal + s.totalDiff

	// ВАЖНО!
	// После деления тотал может быть 25.25, либо просто круглый 24, 60
	tipTotal = correctTotal(tipTotal, roundUp)

	basicGoalsPerMinute := tipTotal / 60

	tipTotalStr := fmt.Sprintf("%.1f", tipTotal)

	if float64(upd.HomeGoals+upd.AwayGoals) > tipTotal {
		return "Тотал пробит", nil, Unwatch
	}

	var isPassed bool

	comment, isPassed = checkGamePace(basicGoalsPerMinute, match, upd, s.fixateGamePaceEveryNMinutes, Lte)
	if !isPassed {
		return comment, nil, Waiting
	}

	// Текущий темп матча опережает рассчитанный на истории
	// проверяем коэффициенты.

	// Задача - найти максимальный тотал >= tipTotal, чтобы коэфф. >= 1.5
	tipTotal = tipTotal - 0.01 // Для удобного сравнивания float

	var (
		maxTotalValue float64
		bestOffer     onexbet.ParamOffer // строковое представление тотала
	)

	for _, offer := range upd.Total.Under {
		if offer.Price < 1.5 {
			continue
		}

		totalValue, err := strconv.ParseFloat(offer.Param, 64)
		if err != nil {
			continue
		}

		if totalValue < tipTotal {
			continue
		}

		if bestOffer.Param == "" {
			// инициализация
			maxTotalValue = totalValue
			bestOffer = offer
		} else {
			if totalValue > maxTotalValue {
				maxTotalValue = totalValue
				bestOffer = offer
			}
		}
	}

	if bestOffer.Param == "" {
		comment = fmt.Sprintf("Тотал %s и больше с коэф. >= 1.5 не найден",
			tipTotalStr)
		return comment, nil, Waiting
	}

	return "", &BetDetails{
		Market: MarketTotal,
		Side:   SideUnder,
		Param:  bestOffer.Param,
		Price:  bestOffer.Price,
	}, Bet
}

func (s HandballFTUnder) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) {
	//buf, _ := json.MarshalIndent(stats, "", "    ")
	//fmt.Printf("%s\n", buf)

	if stats.Status != onexbet.StatusMatchCompleted {
		return
	}

	tipTotal, err := strconv.ParseFloat(tip.Param, 64)
	if err != nil {
		return
	}

	res := &TipResult{
		Result: fmt.Sprintf("счет: %d-%d", stats.HomePoints, stats.AwayPoints),
	}

	goals := float64(stats.HomePoints + stats.AwayPoints)

	// Сперва проверяем что тоталы не равны
	dif := goals - tipTotal

	eps := 0.0001
	if math.Abs(dif) < eps {
		// Равны - расход
		res.Status = model.Void
	} else {
		if goals < tipTotal {
			res.Status = model.Won
		} else {
			res.Status = model.Lost
		}
	}
	return res
}

// P1

type HandballP1Over struct {
	id                          int64
	notifyInTelegram            bool
	totalDiff                   float64
	fixateGamePaceEveryNMinutes int
	isDisabled                  bool
}

func NewHandballP1Over(opt HandballStrategyOptions) HandballP1Over {
	if opt.ID == 0 {
		panic("StrategyID not defined")
	}
	s := HandballP1Over{}
	s.id = opt.ID
	s.notifyInTelegram = opt.NotifyInTelegram
	s.totalDiff = opt.TotalDiff
	s.fixateGamePaceEveryNMinutes = opt.FixateGamePaceEveryNMinutes
	s.isDisabled = opt.Disable

	return s
}

func (s HandballP1Over) ID() int64 {
	return s.id
}

func (s HandballP1Over) ShortName() string {
	return fmt.Sprintf("#%d P1 Over (-%v, %d min)", s.id, s.totalDiff, s.fixateGamePaceEveryNMinutes)
}

func (s HandballP1Over) IsNotifyInTelegram() bool {
	return s.notifyInTelegram
}

func (s HandballP1Over) GetTelegramTipPattern() string {
	return `Сигнал # %d.
Алгоритм %d. Тотал Больше в 1м тайме.
%s. %s
%s - %s

Точка входа %.1f кф. %.3f

Рекомендация: тотал больше %.1f и ниже (%.1f; %.1f)`
}

func (s HandballP1Over) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
	if liveMatch.CurrentPeriod == 1 {
		return true
	}
	return false
}

func (s HandballP1Over) IsInteresting(match *TeamMatch) (notes interface{}, _ bool) {
	if s.isDisabled {
		return
	}
	eps := 0.01
	// Проверки на то, что полные данные

	if match.BasicTotal < 1 {
		return nil, false
	}

	var tipTotal float64

	/*
		if match.H1BasicTotal < 1 {
			tipTotal = (match.BasicTotal / 2) - s.totalDiff
		} else {
			tipTotal = match.H1BasicTotal - s.totalDiff
		}
	*/

	if match.H1BasicTotal < 1 {
		return
	}

	tipTotal = match.H1BasicTotal - s.totalDiff

	// ВАЖНО!
	// После деления тотал может быть 25.25, либо просто круглый 24, 60
	tipTotal = correctTotal(tipTotal, roundDown)

	basicGoalsPerMinute := tipTotal / 30

	if math.Abs(match.H2HGoalsPerMinute) > eps {
		// Если команды между собой забивают меньше чем дает бук (с учетом погрешности)
		// - матч не интересен
		if match.H2HGoalsPerMinute < basicGoalsPerMinute {
			return
		}
	} else {
		if math.Abs(match.HomeGoalsPerMinute) < eps {
			return
		}

		if math.Abs(match.AwayGoalsPerMinute) < eps {
			return
		}
		// Считаем средний темп с которым играют обе команды
		avgGoalsPerMinute := (match.HomeGoalsPerMinute + match.AwayGoalsPerMinute) / 2

		// Если в среднем команды забивают меньше чем дает бук (с учетом погрешности)
		// - матч не интересен
		if avgGoalsPerMinute < basicGoalsPerMinute {
			return
		}
	}

	return nil, true
}

func (s HandballP1Over) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (comment string, _ *BetDetails, _ TipCode) {
	if upd.CurrentTime == 1800 {
		return "в перерыве не ставим (багфикс)", nil, Waiting
	}

	if upd.CurrentTime < p1TimeFrame.Since {
		return "еще рано", nil, Waiting
	}

	if upd.CurrentTime > p1TimeFrame.Until {
		return "время вышло", nil, Unwatch
	}

	var tipTotal float64

	if match.H1BasicTotal < 1 {
		tipTotal = (match.BasicTotal / 2) - s.totalDiff
	} else {
		tipTotal = match.H1BasicTotal - s.totalDiff
	}

	// ВАЖНО!
	// После деления тотал может быть 25.25, либо просто круглый 24, 60
	tipTotal = correctTotal(tipTotal, roundDown)

	basicGoalsPerMinute := tipTotal / 30

	tipTotalStr := fmt.Sprintf("%.1f", tipTotal)

	if float64(upd.HomeGoals+upd.AwayGoals) > tipTotal {
		return "Тотал пробит", nil, Unwatch
	}

	var isPassed bool

	comment, isPassed = checkGamePace(basicGoalsPerMinute, match, upd, s.fixateGamePaceEveryNMinutes, Gte)
	if !isPassed {
		return comment, nil, Waiting
	}

	// Текущий темп матча опережает рассчитанный на истории
	// проверяем коэффициенты.

	// Задача - найти минимальный тотал <= tipTotal, чтобы коэфф. >= 1.5
	tipTotal = tipTotal + 0.01 // Для удобного сравнивания float

	//fmt.Printf("tipTotal: %.2f\n", tipTotal)

	var (
		minTotalValue float64
		bestOffer     onexbet.ParamOffer // строковое представление тотала
	)

	for _, offer := range upd.H1.Total.Over {
		if offer.Price < 1.5 {
			continue
		}

		totalValue, err := strconv.ParseFloat(offer.Param, 64)
		if err != nil {
			continue
		}

		if totalValue > tipTotal {
			//fmt.Printf("totalValue > tipTotal: %.2f > %.2f\n", totalValue, tipTotal)
			continue
		}

		//fmt.Printf("Best: totalValue: %2.f (%s), price: %.2f\n", totalValue, offer.Param, offer.Price)

		if bestOffer.Param == "" {
			// инициализация
			minTotalValue = totalValue
			bestOffer = offer
		} else {
			if totalValue < minTotalValue {
				minTotalValue = totalValue
				bestOffer = offer
			}
		}
	}

	if bestOffer.Param == "" {
		comment = fmt.Sprintf("Тотал %s и меньше с коэф. >= 1.5 не найден",
			tipTotalStr)
		return comment, nil, Waiting
	}

	return "", &BetDetails{
		Market: MarketTotalH1,
		Side:   SideOver,
		Param:  bestOffer.Param,
		Price:  bestOffer.Price,
	}, Bet
}

func (s HandballP1Over) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) {
	//buf, _ := json.MarshalIndent(stats, "", "    ")
	//fmt.Printf("%s\n", buf)

	if stats.Status != onexbet.StatusMatchCompleted {
		return
	}

	h1, ok := stats.ScoreByPeriods[1]
	if !ok {
		return
	}

	tipTotal, err := strconv.ParseFloat(tip.Param, 64)
	if err != nil {
		return
	}

	res := &TipResult{
		Result: fmt.Sprintf("1й тайм: %d-%d", h1.HomePoints, h1.AwayPoints),
	}

	goals := float64(h1.HomePoints + h1.AwayPoints)

	// Сперва проверяем что тоталы не равны
	dif := goals - tipTotal

	eps := 0.0001
	if math.Abs(dif) < eps {
		// Равны - расход
		res.Status = model.Void
	} else {
		if goals > tipTotal {
			res.Status = model.Won
		} else {
			res.Status = model.Lost
		}
	}
	return res
}

type HandballP1Under struct {
	id                          int64
	notifyInTelegram            bool
	totalDiff                   float64
	fixateGamePaceEveryNMinutes int
	isDisabled                  bool
}

func NewHandballP1Under(opt HandballStrategyOptions) HandballP1Under {
	if opt.ID == 0 {
		panic("StrategyID not defined")
	}
	s := HandballP1Under{}
	s.id = opt.ID
	s.notifyInTelegram = opt.NotifyInTelegram
	s.totalDiff = opt.TotalDiff
	s.fixateGamePaceEveryNMinutes = opt.FixateGamePaceEveryNMinutes
	s.isDisabled = opt.Disable

	return s
}

func (s HandballP1Under) ID() int64 {
	return s.id
}

func (s HandballP1Under) ShortName() string {
	return fmt.Sprintf("#%d P1 Under (+%v, %d min)", s.id, s.totalDiff, s.fixateGamePaceEveryNMinutes)
}

func (s HandballP1Under) IsNotifyInTelegram() bool {
	return s.notifyInTelegram
}

func (s HandballP1Under) GetTelegramTipPattern() string {
	return `Сигнал # %d.
Алгоритм %d. Тотал Меньше в 1м тайме.
%s. %s
%s - %s

Точка входа %.1f кф. %.3f

Рекомендация: тотал меньше %.1f и выше (%.1f; %.1f)`
}

func (s HandballP1Under) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
	if liveMatch.CurrentPeriod == 1 {
		return true
	}
	return false
}

func (s HandballP1Under) IsInteresting(match *TeamMatch) (notes interface{}, _ bool) {
	if s.isDisabled {
		return
	}
	eps := 0.01
	// Проверки на то, что полные данные

	if match.BasicTotal < 1 {
		return nil, false
	}

	var tipTotal float64

	/*
		if match.H1BasicTotal < 1 {
			tipTotal = (match.BasicTotal / 2) + s.totalDiff
		} else {
			tipTotal = match.H1BasicTotal + s.totalDiff
		}
	*/

	if match.H1BasicTotal < 1 {
		return
	}

	tipTotal = match.H1BasicTotal + s.totalDiff

	// ВАЖНО!
	// После деления тотал может быть 25.25
	tipTotal = correctTotal(tipTotal, roundUp)

	basicGoalsPerMinute := tipTotal / 30

	if math.Abs(match.H2HGoalsPerMinute) > eps {
		// Если команды между собой забивают больше чем дает бук (с учетом погрешности)
		// - матч не интересен (ибо опасно)
		if match.H2HGoalsPerMinute > basicGoalsPerMinute {
			return
		}
	} else {
		if math.Abs(match.HomeGoalsPerMinute) < eps {
			return
		}

		if math.Abs(match.AwayGoalsPerMinute) < eps {
			return
		}
		// Считаем средний темп с которым играют обе команды
		avgGoalsPerMinute := (match.HomeGoalsPerMinute + match.AwayGoalsPerMinute) / 2

		// Если в среднем команды забивают больше чем дает бук (с учетом погрешности)
		// - матч не интересен (ибо опасно)
		if avgGoalsPerMinute > basicGoalsPerMinute {
			return
		}
	}

	return nil, true
}

func (s HandballP1Under) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (comment string, _ *BetDetails, _ TipCode) {
	if upd.CurrentTime == 1800 {
		return "в перерыве не ставим (багфикс)", nil, Waiting
	}

	if upd.CurrentTime < p1TimeFrame.Since {
		return "еще рано", nil, Waiting
	}

	if upd.CurrentTime > p1TimeFrame.Until {
		return "время вышло", nil, Unwatch
	}

	var tipTotal float64

	if match.H1BasicTotal < 1 {
		tipTotal = (match.BasicTotal / 2) + s.totalDiff
	} else {
		tipTotal = match.H1BasicTotal + s.totalDiff
	}

	// ВАЖНО!
	// После деления тотал может быть 25.25
	tipTotal = correctTotal(tipTotal, roundUp)

	basicGoalsPerMinute := tipTotal / 30

	tipTotalStr := fmt.Sprintf("%.1f", tipTotal)

	if float64(upd.HomeGoals+upd.AwayGoals) > tipTotal {
		return "Тотал пробит", nil, Unwatch
	}

	var isPassed bool

	comment, isPassed = checkGamePace(basicGoalsPerMinute, match, upd, s.fixateGamePaceEveryNMinutes, Lte)
	if !isPassed {
		return comment, nil, Waiting
	}

	// Задача - найти максимальный тотал >= tipTotal, чтобы коэфф. >= 1.5
	tipTotal = tipTotal - 0.01 // Для удобного сравнивания float

	var (
		maxTotalValue float64
		bestOffer     onexbet.ParamOffer // строковое представление тотала
	)

	for _, offer := range upd.H1.Total.Under {
		if offer.Price < 1.5 {
			continue
		}

		totalValue, err := strconv.ParseFloat(offer.Param, 64)
		if err != nil {
			continue
		}

		if totalValue < tipTotal {
			continue
		}

		if bestOffer.Param == "" {
			// инициализация
			maxTotalValue = totalValue
			bestOffer = offer
		} else {
			if totalValue > maxTotalValue {
				maxTotalValue = totalValue
				bestOffer = offer
			}
		}
	}

	if bestOffer.Param == "" {
		comment = fmt.Sprintf("Тотал %s и больше с коэф. >= 1.5 не найден",
			tipTotalStr)
		return comment, nil, Waiting
	}

	return "", &BetDetails{
		Market: MarketTotalH1,
		Side:   SideUnder,
		Param:  bestOffer.Param,
		Price:  bestOffer.Price,
	}, Bet
}

func (s HandballP1Under) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) {
	//buf, _ := json.MarshalIndent(stats, "", "    ")
	//fmt.Printf("%s\n", buf)

	if stats.Status != onexbet.StatusMatchCompleted {
		return
	}

	h1, ok := stats.ScoreByPeriods[1]
	if !ok {
		return
	}

	//if stats.H1 == nil || stats.H2 == nil {
	//return
	//}

	tipTotal, err := strconv.ParseFloat(tip.Param, 64)
	if err != nil {
		return
	}

	res := &TipResult{
		Result: fmt.Sprintf("1й тайм: %d-%d", h1.HomePoints, h1.AwayPoints),
	}

	goals := float64(h1.HomePoints + h1.AwayPoints)

	// Сперва проверяем что тоталы не равны
	dif := goals - tipTotal

	eps := 0.0001
	if math.Abs(dif) < eps {
		// Равны - расход
		res.Status = model.Void
	} else {
		if goals < tipTotal {
			res.Status = model.Won
		} else {
			res.Status = model.Lost
		}
	}
	return res
}
