package battleon

import (
	"context"
	"fmt"
	"strings"
	"time"

	"wartank/pkg/components/section"
	"wartank/pkg/components/sectionnet"
	"wartank/pkg/types"
	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/health"
	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/manevr"
	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/shot"
	"wartank/server/serv_bots/warbot/angar/battle/battle_worker/battleon/shot/isshot"
)

/*
	Предоставляет сетевой компонент при непосредственном сражении
*/

// BattleOn -- непосредственно танкует в сражении
type BattleOn struct {
	*section.Section
	net            *sectionnet.SectionNet
	server         types.IServer
	bot            types.IServBot
	ctxBattle      context.Context // Контекст сражения
	fnCancelBattle func()          // Функция отмены сражения

	shot   *shot.Shot     // Объект выстрела
	health *health.Health // Текущее здоровье танка
	manevr *manevr.Manevr // Возможность маневрирования
	login  string
	isShot *isshot.IsShot // Признак необходимости маскирования (запрет стрельбы, когда слабое здоровье)
	chTick chan int       // Ежесекудная проверка на окончание сражения
}

// NewBattleOn -- возвращает новый *BattleOn
func NewBattleOn(server types.IServer, bot types.IServBot) (*BattleOn, error) {
	{ // Предусловия
		if server == nil {
			return nil, fmt.Errorf("NewBattleOn(): IServer == nil")
		}
		if bot == nil {
			return nil, fmt.Errorf("NewBattleOn():IServBot == nil")
		}
	}
	ctxBattle, fnCancelBattle := context.WithTimeout(server.CtxApp(), time.Second*305)
	sf := &BattleOn{
		server:         server,
		bot:            bot,
		ctxBattle:      ctxBattle,
		fnCancelBattle: fnCancelBattle,
		login:          bot.Name(),
		isShot:         isshot.NewIsShot(),
		chTick:         make(chan int, 2),
	}
	var err error
	{ // ISection (ожидание)
		sf.Section, err = section.NewSection(server, `<title>Сражения</title>`)
		if err != nil {
			return nil, fmt.Errorf("NewBattleOn(): in create *Section, err=\n\t%w", err)
		}
	}
	go sf.makeTick()
	go sf.run()
	return sf, nil
}

// Ежесекудный тик
func (sf *BattleOn) makeTick() {
	defer func() {
		close(sf.chTick)
	}()
	for {
		select {
		case <-sf.server.Done(): // Отмена контекста приложения
			sf.fnCancelBattle()
			return
		case <-sf.ctxBattle.Done(): // Битва закончилась
			return
		default:
			sf.chTick <- 1
			time.Sleep(time.Second * 1)
		}
	}
}

// запускает сражение
func (sf *BattleOn) run() {
	{ // Создание сети
		sf.net = sectionnet.NewSectionNet(sf.server, sf.bot, sf.bot.Angar().Battle(), "http://wartank.ru/pve")
		sf.net.Run()
	}
	defer func() {
		sf.fnCancelBattle()
		// log._rintf("BattleOn.run(): сражение завершено\n")
	}()
	{ // Подготовка к сражению
		var err error
		sf.shot, err = shot.NewShot(sf, sf.login) // Объект выстрела
		if err != nil {
			// log._rintf("ERRO BattleOn.Run(): при создании выстрела танка, err=\n\t%v\n", err)
			time.Sleep(time.Second * 1)
			return
		}
		sf.health, err = health.NewHealth(sf, sf.login)
		if err != nil {
			// log._rintf("ERRO BattleOn.Run(): при создании здоровья танка, err=\n\t%v\n", err)
			time.Sleep(time.Second * 1)
			return
		}
		sf.manevr, err = manevr.NewManevr(sf)
		if err != nil {
			// log._rintf("ERRO BattleOn.Run(): при создании маневра танка, err=\n\t%v\n", err)
			time.Sleep(time.Second * 1)
			return
		}
	}
	for { // Рабочий цикл сражения
		select {
		case <-sf.server.Done(): // Прерывание работы от приложения
			return
		case <-sf.CountDown().ChanSig(): // Вышло время (сигнал от секции)
			return
		case <-sf.ctxBattle.Done():
			return
		case <-sf.chTick: // Ежесекундный сигнал в сражении
			sf.checkEnd()
		}
	}
}

// Net -- возвращает сетевой компонент секции
func (sf *BattleOn) Net() types.ISectionNet {
	return sf.net
}

// Проверяет окончание сражения
func (sf *BattleOn) checkEnd() {
	// log._rintf("BattleOn.checkEnd()\n")
	fnPrintEnd := func() {
		sf.fnCancelBattle()
		// log._rintf("BattleOn.checkEnd(): сражение завершено\n")
	}

	if err := sf.net.UpdateLst("Сражение+"); err != nil {
		// log._rintf("WARN BattleOn.checkEnd(): при обновлении lstBattleOn, err=\n\t%v\n", err)
		fnPrintEnd()
		return
	}
	lstBattleOn := sf.GetLst()
	for _, strOut := range lstBattleOn { // Сражение ещё идёт
		if strings.Contains(strOut, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`) {
			return
		}
	}
	// Сражение закончилось
	fnPrintEnd()
}

func (sf *BattleOn) SetNeedManevr() {
	if sf.manevr == nil {
		return
	}
	sf.manevr.SetNeed()
}

// Masking -- признак запрета стрельбы при слабом здоровье
func (sf *BattleOn) Masking() *isshot.IsShot {
	return sf.isShot
}

// Ctx -- возвращает контекст отмены сражения
func (sf *BattleOn) Ctx() context.Context {
	return sf.ctxBattle
}

// CancelBattle - -вызов функции отмены контекста сражения
func (sf *BattleOn) CancelBattle() {
	sf.fnCancelBattle()
}
