package health

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/health/healthtime"
	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/health/isrepair"
	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/health/repairtime"

	// "wartank/pkg/components/sound"
	"wartank/pkg/types"
)

/*
	Контролирует состояние здоровья танка
*/

// Health -- контроль здоровья танка
type Health struct {
	types.IBattleOn
	temp       *healthtime.HealthTime // Изменяемое здоровье танка
	full       *healthtime.HealthTime // Полное здоровье танка
	isRepair   *isrepair.IsRepair     // Необходимость восстановления
	repairTime *repairtime.RepairTime // Время до восстановления
	login      string                 // Для поиска контрольных строк
	chTick     chan int               // Канал для ровной отправки тиков
	deltaOld   int                    // Старая дельта потери здоровья
}

// NewHealth -- возвращает новый *Health
func NewHealth(battle types.IBattleOn, login string) (*Health, error) {
	{ // Предусловия
		if battle == nil {
			return nil, fmt.Errorf("NewHealth(): battle is nil")
		}
		if login == "" {
			return nil, fmt.Errorf("NewHealth(): login is empty")
		}
	}
	sf := &Health{
		IBattleOn:  battle,
		temp:       healthtime.NewHealthTime(),
		full:       healthtime.NewHealthTime(),
		isRepair:   isrepair.NewIsRepair(),
		repairTime: repairtime.NewRepairTime(),
		login:      login,
		chTick:     make(chan int, 2),
	}
	go sf.makeTik()
	go sf.run()
	return sf, nil
}

// Отправляе ттики с заданным равным интервалом
func (sf *Health) makeTik() {
	defer func() {
		close(sf.chTick)
		sf.CancelBattle()
		// log._rintf("Health.makeTick(): сражение завершёно\n")
	}()
	countLow := 0
	for {
		select {
		case <-sf.Ctx().Done():
			return
		default:
			if sf.IsDeath() {
				return
			}
			switch sf.repairTime.Get() <= 0 {
			case true:
				countLow++
			default:
				countLow = 0
			}
			if countLow > 90 {
				return
			}
		}
		sf.chTick <- 1
		time.Sleep(time.Second * 1)
		sf.repairTime.Dec()
	}
}

// Главный цикл обработки здоровья в сражении
func (sf *Health) run() {
	for range sf.chTick {
		if err := sf.findHealth(); err != nil { // Найти свой здоровье
			log.Printf("Health.run(): при попытке найти здоровье, err=\n\t%v\n", err)
		}
		sf.findRepairTime()
		if sf.Masking().Get() {
			if !sf.isRepair.Get() {
				continue
			}
			sf.repair()
		}
	}
}

// Full -- возвращает объект полного здоровья танка
func (sf *Health) Full() int {
	return sf.full.Get()
}

// IsDeath -- возвращает признак мертвичины танка
func (sf *Health) IsDeath() bool {
	lstBattle := sf.GetLst()
	for _, strOut := range lstBattle {
		if strings.Contains(strOut, `>Ваш танк подбит.`) {
			// log._rintf("INFO Health.repair(): танк подбит\n")
			sf.temp.Set(0)
			return true
		}
	}
	return false
}

// Ищет время восстановления ремки
func (sf *Health) findRepairTime() {
	defer func() {
		if sf.repairTime.IsReady() {
			return
		}
		if sf.repairTime.IsChange() {
			log.Printf("Health.findRepair(): до ремки=%v\n", sf.repairTime.Get())
		}
	}()
	if sf.repairTime.IsReady() {
		time.Sleep(time.Second * 1)
		return
	}
	var (
		strOut    string
		lstBattle = sf.GetLst()
		isFind    bool
		ind       int
	)
	// <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>12 секунд</span></span></a>
	//
	for ind, strOut = range lstBattle {
		if !strings.Contains(strOut, `ILinkListener-currentControl-repairLink`) {
			continue
		}
		if strings.Contains(strOut, ` секунд</span></span></a>`) {
			isFind = true
			break
		}
	}
	if !isFind {
		time.Sleep(time.Second * 1)
		return
	}
	strOut = lstBattle[ind]
	// <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>12 секунд</span></span></a>
	lstTime := strings.Split(strOut, `ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>`)
	if len(lstTime) < 2 {
		// log._rintf("ERRO Health.findRepair(): при попытке получить ссылку на ремонт, strOut=\n%v\n", strOut)
		time.Sleep(time.Second * 1)
		return
	}
	strTime := lstTime[1]
	lstTime = strings.Split(strTime, ` секунд</span></span></a>`)
	strTime = lstTime[0]
	if err := sf.repairTime.Set(strTime); err != nil {
		// log._rintf("ERRO Health.findRepair(): при установке времени восстановления ремки, err=\n\t%v\n", err)
		time.Sleep(time.Second * 1)
	}
}

// Восстановливает здоровье (~)
func (sf *Health) repair() {
	var (
		strOut       string
		lstBattleOn  = sf.GetLst()
		isFindRepair bool
		ind          int
	)
	// <span>Ремкомплект</span>
	// <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>Ремкомплект</span></span></a>
	for ind, strOut = range lstBattleOn {
		if strings.Contains(strOut, `<span>Ремкомплект</span>`) {
			isFindRepair = true
			break
		}
	}
	if !isFindRepair {
		time.Sleep(time.Second * 1)
		return
	}
	strOut = lstBattleOn[ind]
	// <a href="pve?6-26.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>Ремкомплект</span></span></a>
	lstLink := strings.Split(strOut, `<a href="`)
	strLink := lstLink[1]
	lstLink = strings.Split(strLink, `" class="simple-but blue"><span><span>Ремкомплект</span></span></a>`)
	strLink = "http://wartank.ru/" + lstLink[0]
	lstBattleOn, err := sf.Net().Get(strLink)
	if err != nil {
		// log._rintf("ERRO Health.repair(): при выполнении GET-команды ремонта, err=\n\t%v\n", err)
		time.Sleep(time.Second * 1)
		return
	}
	if err = sf.Update(lstBattleOn); err != nil {
		// log._rintf("ERRO Health.repair(): при обновлении lstBattle, err=\n\t%v\n", err)
		time.Sleep(time.Second * 1)
		return
	}
	// sound.Repair()
	sf.setHealth(sf.full.Get())
	// log._rintf("INFO Health.repair(): здоровье восстановлено\n")
}

// Ищет своё здоровье (~)
func (sf *Health) findHealth() error {
	var (
		ind       int
		strOut    string
		isFind    bool
		lstBattle = sf.GetLst()
	)
	if len(lstBattle) == 0 { // Принудительно обновим сражение
		if err := sf.Net().UpdateLst("Сражение+ Здоровье"); err != nil {
			time.Sleep(time.Second * 1)
			return fmt.Errorf("Health.findHealth(): пустой lsBattleOn, err=\n\t%w", err)
		}
	}
	for ind, strOut = range lstBattle {
		if strings.Contains(strOut, `alt="`+sf.login+`"`) {
			isFind = true
			break
		}
	}
	if !isFind { // Свой танк не найден
		time.Sleep(time.Second * 1)
		return fmt.Errorf("Health.findHealth(): своё здоровье не найдено")
	}
	// Свой танк найден, ищем здоровье
	ind += 11
	strOut = lstBattle[ind]
	lstHealth := strings.Split(strOut, `<div class="value-block lh1"><span><span>`)
	strHealth := lstHealth[1]
	lstHealth = strings.Split(strHealth, `</span></span></div>`)
	strHealth = lstHealth[0]
	iHealth, err := strconv.Atoi(strHealth)
	if err != nil {
		time.Sleep(time.Second * 1)
		return fmt.Errorf("Health.findHealth(): здоровье(%v) не число, err=%w", strHealth, err)
	}
	sf.setHealth(iHealth)
	return nil
}

// setHealth -- устанавливает текущее здоровье
func (sf *Health) setHealth(val int) {
	if val < 0 {
		// log._rintf("WARN Health.setHealth(): кривое значение здоровья танка(%v)\n", val)
		val = 0
		return
	}

	if val > sf.full.Get() {
		// log._rintf("WARN Health.setHealth(): кривое текущее здоровье, %v/%v\n", val, sf.full.Get())
		sf.full.Set(val)
		sf.temp.Set(val)
		sf.deltaOld = 0
		sf.Masking().Reset()
		sf.isRepair.Reset()
		return
	}

	delta := sf.temp.Get() - val
	if delta > 0 { // Дельта будет больше нуля, если только
		if delta != sf.deltaOld {
			// log._rintf("INFO Health.setHealth(): потеря здоровья=%v/%v\n", -delta, val)
			sf.deltaOld = delta
			sf.temp.Set(val)
		}
	}

	switch {
	case val == 0:
		sf.temp.Set(0)
		sf.CancelBattle()
		return
	case val <= 500: // Запретить стрельбу
		sf.Masking().Set() // Установить запрет стрельбы пока слабое здоровье
		sf.isRepair.Set()
		// log._rintf("WARN Health.setHealth(): низкий уровень здоровья(%v)\n", val)
		sf.SetNeedManevr()
	case val > 500: // Разрешить стрельбы
		sf.Masking().Reset()
		sf.isRepair.Reset()
		if delta > sf.full.Get()*4/10 { // Проверить на критичность падения здоровья на 40%
			// log._rintf("WARN Health.setHealth(): большая разовая потеря здоровья(%v)\n", delta)
			sf.isRepair.Set()
			sf.SetNeedManevr()
			return
		}
	}
}
