package shot

import (
	"context"
	"fmt"

	// "log"
	"strconv"
	"strings"
	"time"
	"wartank/server/serv_bots/warbot/angar/division/div_war/div_war_on/shot/damage"
	"wartank/server/serv_bots/warbot/angar/division/div_war/div_war_on/shot_time"

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

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

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

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

// NewShot -- возвращает новый *Shot
func NewShot(divWar types.ИДивизияВойнаДействие, 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")
		}
	}
	сам := &Shot{
		ИДивизияВойнаДействие: divWar,
		ctxEnd:   divWar.Ctx(),
		recharge: shot_time.NewShotTime(),
		damage:   damage.NewDamage(),
		isEnd:    divWar.ЕслиКонец(),
		login:    login,
		chTick:   make(chan int, 2),
	}
	// Установить время перезарядки
	сам.recharge.Set(6800)
	go сам.makeTick()
	go сам.run()
	return сам, nil
}

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

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

// Обновляет возможность выстрела (~)
//
//	Вызывается из отдельного потока
func (сам *Shot) shot() {
	сам.Сеть().Обновить()
	var (
		strOut      string
		lstBattle   = сам.СписПолучить()
		еслиНайдено 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`) {
			еслиНайдено = true
			break
		}
	}
	if !еслиНайдено {
		// log._rintf("WARN Shot.shot(): не найдены ссылка на выстрел\n")
		сам.isEnd.Уст()
		сам.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 = "https://wartank.ru/" + lstLink[0]
	lstBattle, err = сам.Сеть().Get(strLink)
	if err != nil {
		// log._rintf("ERRO Shot.shot(): при исполнении GET-команды выстрела обычным снарядом, err=\n\t%v\n", err)
		сам.isEnd.Уст()
		сам.CancelBattle()
		return
	}
	if err = сам.СтрОбновить(lstBattle); err != nil {
		// log._rintf("ERRO Shot.shot(): при обновлении lstBattle, err=\n\t%v\n", err)
		сам.isEnd.Уст()
		сам.CancelBattle()
		return
	}
	// sound.Shot()
	сам.Манёвр()
}

// Ищет урон от выстрела
func (сам *Shot) findDamage() {
	var (
		ind         int
		еслиНайдено bool
		lstShot     = сам.СписПолучить()
		strOut      string
	)

	for ind, strOut = range lstShot {
		// <span class="yellow1 td_u">prospero tank</span>
		if strings.Contains(strOut, `<span class="yellow1 td_u">`+сам.login+`</span>`) {
			ind += 2
			strOut = lstShot[ind]
			еслиНайдено = true
			break
		}
	}
	if !еслиНайдено { // Возможно был рикошет или манёвр
		// 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 { // Кривой урон от выстрела
		сам.recharge.Dec5()
		// log._rintf("WARN Shot.findDamage(): ошибка в значении урона(%v)\n", iDamage)
		iDamage = 0
	}
	сам.damageSum += alias.Урон(iDamage)
	// log._rintf("INFO Shot.Damage(): damageSum=%v\n", сам.damageSum)
	if iDamage < 70 {
		сам.damage.Set(alias.Урон(iDamage))
		сам.recharge.Inc210()
	}
	// log._rintf("INFO Shot.findDamage(): выстрел=+%v, урон=%v", iDamage, сам.damageSum)
	if iDamage == 0 {
		return
	}
	сам.setDamage(alias.Урон(iDamage))
}

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

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