package shot

import (
	"context"
	"fmt"

	// "log"
	"strconv"
	"strings"
	"time"

	"wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/shot/damage"
	"wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/shottime"

	// "wartank/internal/components/sound"
	"wartank/pkg/alias"
	"wartank/pkg/components/safebool"
	"wartank/pkg/types"
)

/*
	Исходник предоставляет выстрел со свойствами:
		- время до выстрела
		- длительность перезарядки

	Первый параметр постоянно изменяется (после выстрела восстанавливается)
	Второй параметр меняется медленно (в зависимости от количества очков после выстрела)
*/

// Shot -- объект выстрела
type Shot struct {
	types.IDivWarOn                    // FIXME:
	recharge        *shottime.ShotTime // Сколько времени нужно для полной перезарядки
	damage          *damage.Damage     // Урон от выстрела с памятью
	damageSum       alias.ADamage      // Суммарный урон
	isEnd           *safebool.SafeBool // Признак конца сражения
	login           string             // Логин для поиска контрольных строк
	chTick          chan int           // Тик для выстрела
	ctxEnd          context.Context    // Признак окончания сражения
}

// NewShot -- возвращает новый *Shot
func NewShot(divWar types.IDivWarOn, login string) (*Shot, error) {
	{ // Предусловия
		if divWar == nil {
			return nil, fmt.Errorf("NewShot(): battle is nil")
		}
		if login == "" {
			return nil, fmt.Errorf("NewShot(): login is empty")
		}
	}
	sf := &Shot{
		IDivWarOn: divWar,
		ctxEnd:    divWar.Ctx(),
		recharge:  shottime.NewShotTime(),
		damage:    damage.NewDamage(),
		isEnd:     divWar.IsEnd(),
		login:     login,
		chTick:    make(chan int, 2),
	}
	// Установить время перезарядки
	sf.recharge.Set(6800)
	go sf.makeTick()
	go sf.run()
	return sf, nil
}

// Генерирует тики, когда можно стрелять
func (sf *Shot) makeTick() {
	defer func() {
		sf.isEnd.Set()
		close(sf.chTick)
		sf.CancelBattle()
		// log._rintf("Shot.makeTick(): сражение завершёно\n")
	}()
	countMasking := 0
	for {
		select {
		case <-sf.ctxEnd.Done():
			return
		default:
			if sf.isEnd.Get() { // Битва закончилась
				return
			}
			switch sf.Masking().Get() { // Проверить запрет на стрельбу при слабом здоровье
			case true:
				// log._rintf("WARN Shot.run(): запрет на выстрел\n")
				countMasking++
				if countMasking >= 200 {
					return
				}
				sf.Manevr()
				time.Sleep(time.Millisecond * 500)
				continue
			default:
				countMasking = 0
			}
			sf.chTick <- 1 // Здесь же первый выстрел
			recharge := sf.recharge.Get()
			// log._rintf("INFO Shot.run() перезарядка=%v msec\n", recharge)
			// Если идёт перезарядка -- постепенно обнуляем время ожидания
			time.Sleep(time.Millisecond * time.Duration(recharge))
		}
	}
}

// Цикл выстрела (в отдельном потоке)
func (sf *Shot) run() {
	for {
		select {
		case <-sf.ctxEnd.Done():
			return
		case <-sf.chTick:
			// Стрелять можно, стандартное ожидание
			sf.shot()
			sf.findDamage()
		}
	}
}

// Обновляет возможность выстрела (~)
//
//	Вызывается из отдельного потока
func (sf *Shot) shot() {
	if err := sf.Net().UpdateLst("Битва дивизий+ выстрел"); err != nil { // Проверка на непосредственно битву
		// <span>закончилась 00:00:07 назад</span>
		// log._rintf("ERRO Shot.shot(): при обновлении lstBattleOn, err=\n\t%v\n", err)
		sf.isEnd.Set()
		sf.CancelBattle()
		return
	}
	var (
		strOut    string
		lstBattle = sf.GetLst()
		isFind    bool
		err       error
	)
	// <a href="pve?6-26.ILinkListener-currentControl-attackRegularShellLink" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>
	for _, strOut = range lstBattle {
		if strings.Contains(strOut, `-currentControl-attackRegularShellLink`) {
			isFind = true
			break
		}
	}
	if !isFind {
		// log._rintf("WARN Shot.shot(): не найдены ссылка на выстрел\n")
		sf.isEnd.Set()
		sf.CancelBattle()
		return
	}
	lstLink := strings.Split(strOut, `<a href="`)
	strLink := lstLink[1]
	lstLink = strings.Split(strLink, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
	strLink = "http://wartank.ru/" + lstLink[0]
	lstBattle, err = sf.Net().Get(strLink)
	if err != nil {
		// log._rintf("ERRO Shot.shot(): при исполнении GET-команды выстрела обычным снарядом, err=\n\t%v\n", err)
		sf.isEnd.Set()
		sf.CancelBattle()
		return
	}
	if err = sf.Update(lstBattle); err != nil {
		// log._rintf("ERRO Shot.shot(): при обновлении lstBattle, err=\n\t%v\n", err)
		sf.isEnd.Set()
		sf.CancelBattle()
		return
	}
	// sound.Shot()
	sf.Manevr()
}

// Ищет урон от выстрела
func (sf *Shot) findDamage() {
	var (
		ind     = 0
		isFind  = false
		lstShot = sf.GetLst()
		strOut  string
	)

	for ind, strOut = range lstShot {
		// <span class="yellow1 td_u">prospero tank</span>
		if strings.Contains(strOut, `<span class="yellow1 td_u">`+sf.login+`</span>`) {
			ind += 2
			strOut = lstShot[ind]
			isFind = true
			break
		}
	}
	if !isFind { // Возможно был рикошет или манёвр
		// log._rintf("WARN Shot.findDamage(): не найден урон от выстрела, strOut=%q, ind=%v\n", strOut, ind)
		return
	}
	lstDamage := strings.Split(strOut, ` на  <span class="red1">`)
	if len(lstDamage) == 1 {
		return
	}
	strDamage := lstDamage[1]
	iDamage, err := strconv.Atoi(strDamage)
	if err != nil {
		// log._rintf("ERRO Shot.findDamage(): damage(%v) не число, err=\n\t%v\n", strDamage, err)
		return
	}
	if iDamage <= 0 { // Кривой урон от выстрела
		sf.recharge.Dec5()
		// log._rintf("WARN Shot.findDamage(): ошибка в значении урона(%v)\n", iDamage)
		iDamage = 0
	}
	sf.damageSum += alias.ADamage(iDamage)
	// log._rintf("INFO Shot.Damage(): damageSum=%v\n", sf.damageSum)
	if iDamage < 70 {
		sf.damage.Set(alias.ADamage(iDamage))
		sf.recharge.Inc210()
	}
	// log._rintf("INFO Shot.findDamage(): выстрел=+%v, урон=%v", iDamage, sf.damageSum)
	if iDamage == 0 {
		return
	}
	sf.setDamage(alias.ADamage(iDamage))
}

// setDamage -- обновляет время перезарядки в зависимости от произведённого урона
func (sf *Shot) setDamage(val alias.ADamage) {
	sf.damage.Set(val)
	switch sf.damage.Result() {
	case "none":
		sf.recharge.Dec5()
	case "up":
		sf.recharge.Dec30()
	case "down":
		sf.recharge.Inc210()
	}
}

// IsEnd -- возвращает объект разрешения стрельбы
func (sf *Shot) IsEnd() *safebool.SafeBool {
	return sf.isEnd
}
