package convoy

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

	"github.com/sirupsen/logrus"

	"wartank/pkg/components/section"
	"wartank/pkg/types"
	"wartank/server/serv_bots/warbot/angar/convoy/convoynet"
	"wartank/server/serv_bots/warbot/tank/tankstat/static_param"
)

/*
	Объект конвоя в ангаре
*/

// Convoy -- объект конвоя в ангаре
type Convoy struct {
	*section.Section
	net      *convoynet.ConvoyNet
	server   types.IServer
	bot      types.IServBot
	glory    types.IStatParam // Количество славы
	chAttack chan int         // Канал проведения атаки
}

// NewConvoy -- возвращает новый *Convoy
func NewConvoy(server types.IServer, bot types.IServBot) (*Convoy, error) {
	sf := &Convoy{
		server:   server,
		bot:      bot,
		glory:    static_param.NewStaticParam("glory"),
		chAttack: make(chan int, 5),
	}
	return sf, nil
}

func (sf *Convoy) Run() error {
	var err error
	{ // Section
		sf.Section, err = section.NewSection(sf.server, `<title>Конвой</title>`)
		if err != nil {
			return fmt.Errorf("Convoy.Run(): in create *Section, err=\n\t%w", err)
		}
	}
	{ // SectionNet
		sf.net, err = convoynet.NewConvoyNet(sf.server, sf.bot)
		if err != nil {
			return fmt.Errorf("Convoy.Run(): in create *SectionNet, err=\n\t%w", err)
		}
		if err := sf.net.Run(); err != nil {
			return fmt.Errorf("Convoy.Run(): in run *SectionNet, err=\n\t%w", err)
		}
	}
	go sf.reservTick()
	go sf.run()
	return nil
}

// UpdateLst -- принудительно обновляет состояние конвоя
func (sf *Convoy) UpdateLst() {
	if err := sf.net.UpdateLst("Конвой"); err != nil {
		log.Printf("Convoy.UpdateLst(): err=\n\t%v\n", err)
	}
}

// Glory --возвращает счётчик славы конвоя
func (sf *Convoy) Glory() types.IStatParam {
	return sf.glory
}

// Если время конвоя замерло -- посылает контрольный сигнал на атаку
func (sf *Convoy) reservTick() {
	defer func() {
		close(sf.chAttack)
	}()
	for {
		select {
		case <-sf.server.Done():
			return
		default:
			ct0 := sf.CountDown().Get()
			time.Sleep(time.Second * 60)
			ct1 := sf.CountDown().Get()
			if ct1 != ct0 {
				continue
			}
			sf.chAttack <- 1
		}
	}
}

// обрабатывает конвой
func (sf *Convoy) run() {
	sf.chAttack <- 1
	for {
		select {
		case <-sf.server.Done():
			sf.CountDown().Stop()
			return
		case <-sf.CountDown().ChanSig(): // Время истекло
			sf.updateTime()
			sf.updateGlory()
		case <-sf.chAttack: // Сигнал к атаке
			sf.attackConvoy() // в этом месте только атаковать
			sf.checkWarForce()
			sf.checkMaster()
			sf.check6frage()
		}
	}
}

// Обновляет славу потребованию
func (sf *Convoy) updateGlory() {
	// Найти строку с упоминанием оставшегося времени конвоя
	lstConvoy := sf.GetLst()
	var (
		strGlory string
		isFind   bool
	)
	for _, lastTime := range lstConvoy {
		if strings.Contains(lastTime, `alt="Слава" title="Слава"> `) {
			strGlory = lastTime
			isFind = true
			break
		}
	}
	if !isFind { // Не найдена строка со славой -- это атака
		return
	}
	// Ищем количество славы
	lstGlory := strings.Split(strGlory, `alt="Слава" title="Слава"> `)
	strGlory = lstGlory[1]
	iGlory, err := strconv.Atoi(strGlory)
	if err != nil {
		// log._rintf("ERRO ConvoyNet.updateGlory(): слава(%v) не число, err=\n\t%v\n", strGlory, err)
		return
	}
	sf.glory.Set(iGlory)
}

// Обновляет оставшееся время конвоя
func (sf *Convoy) updateTime() {
	// Время подходит надо обновляться
	if err := sf.net.UpdateLst("Конвой"); err != nil {
		logrus.WithError(err).Error("ConvoyNet.updateTime(): при выполнении GET-команды обновления")
		sf.CountDown().Set(20)
		return
	}
	// Найти строку с упоминанием оставшегося времени конвоя
	lstConvoy := sf.GetLst()
	var (
		strLastTime string
		isFind      bool
		isMask      bool
	)
	for _, lastTime := range lstConvoy {
		if strings.Contains(lastTime, `До следующего конвоя: `) {
			strLastTime = lastTime
			isFind = true
			break
		}
		if strings.Contains(lastTime, `Полная маскировка через `) {
			strLastTime = lastTime
			isMask = true
			break
		}
		// <div class="bot"><a class="simple-but border red" w:id="startFight" href="convoy?7-1.ILinkListener-root-startFight"><span><span>В БОЙ!</span></span></a></div>
		if strings.Contains(lastTime, `ILinkListener-root-startFight`) {
			sf.chAttack <- 1
			return
		}
		if strings.Contains(lastTime, `ILinkListener-root-findEnemy`) {
			sf.chAttack <- 1
			return
		}
		// <div class="bot"><a class="simple-but border" w:id="startMasking" href="convoy?12-1.ILinkListener-root-startMasking"><span><span>В БОЙ!</span></span></a></div>
		if strings.Contains(lastTime, `ILinkListener-root-startMasking`) {
			sf.chAttack <- 1
			return
		}
	}
	switch {
	case isFind: // Большая пауза между конвоями
		// Ждём окончания ожидания конвоя
		lstTime := strings.Split(strLastTime, `До следующего конвоя: `)
		strLastTime = lstTime[1]
		if err := sf.CountDown().Parse(strLastTime); err != nil {
			// log._rintf("WARN Convoy.updateTime(): при установке времени ожидания конвоя(%v)\n\terr=%v\n", strLastTime, err)
			sf.CountDown().Set(10)
		}
	case isMask: // Если маскировка между конвоями
		// Ждём окончания ожидания конвоя
		lstTime := strings.Split(strLastTime, `Полная маскировка через `)
		strLastTime = lstTime[1]
		if err := sf.CountDown().Parse(strLastTime); err != nil {
			// log._rintf("ERRO Bank.getAllMode(): при установке времени банка для 1го режима(%v)\n\terr=%v\n", strLastTime, err)
			sf.CountDown().Set(10)
		}
	}
}

// Проводит атаку на конвой
func (sf *Convoy) attackConvoy() {
	// Найти контрольную строку
	var (
		strOut    = ""
		lstConvoy = sf.GetLst()
		isFind    bool
	)

	for _, strLink := range lstConvoy {
		if strings.Contains(strLink, `>Начать разведку<`) {
			strOut = strLink
			lstLink := strings.Split(strOut, `<div class="bot"><a class="simple-but border" w:id="findEnemy" href="`)
			strOut = lstLink[1]
			lstLink = strings.Split(strOut, `"><span><span>Начать разведку</span></span></a></div>`)
			strOut = "http://wartank.ru/" + lstLink[0]
			isFind = true
			break
		}
		if strings.Contains(strLink, `<span>В БОЙ!</span>`) {
			strOut = strLink
			lstLink := strings.Split(strOut, `<div class="bot"><a class="simple-but border" w:id="startMasking" href="`)
			if len(lstLink) == 1 {
				lstLink = strings.Split(strOut, `<div class="bot"><a class="simple-but border red" w:id="startFight" href="`)
			}
			strOut = lstLink[1]
			lstLink = strings.Split(strOut, `"><span><span>В БОЙ!</span></span></a></div>`)
			strOut = "http://wartank.ru/" + lstLink[0]
			isFind = true
			break
		}
		if strings.Contains(strLink, `>ОБЫЧНЫЕ<`) {
			strOut = strLink
			lstLink := strings.Split(strOut, `<a href="`)
			strOut = lstLink[1]
			lstLink = strings.Split(strOut, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
			strOut = "http://wartank.ru/" + lstLink[0]
			isFind = true
			break
		}
	}
	if !isFind { // Время ожидания
		if err := sf.CountDown().Set(1); err != nil {
			panic(fmt.Errorf("ConvoyNet.attackConvoy(): пр установке CountDown, err=\n\t%w", err))
		}
		return
	}
	strLink := strOut
	// Можно начать разведку
	lstConvoy, err := sf.net.Get(strLink)
	if err != nil {
		// log._rintf("ERRO ConvoyNet.attackConvoy(): при выполнении GET-команды 'В атаку!', err=\n\t%v\n", err)
		return
	}
	if err = sf.Update(lstConvoy); err != nil {
		for _, strOut = range lstConvoy {
			if strings.Contains(strOut, `<title>Ошибка на сервере. Сообщение админу уже отправлено.</title>`) {
				// log._rintf("ERRO ConvoyNet.attackConvoy(): при обновлении lstConvoy, strOut=\n\t%v\n", strOut)
				return
			}
		}
		for _, strOut = range lstConvoy {
			if strings.Contains(strOut, `<title>База</title>`) {
				// log._rintf("ERRO ConvoyNet.attackConvoy(): при обновлении lstConvoy (найдено lstBase), strOut=\n\t%v\n", strOut)
				return
			}
		}
		// log._rintf("ERRO ConvoyNet.attackConvoy(): при обновлении lstConvoy, err=\n\t%v\n", err)
		return
	}
	for sf.attack() {
	}
	if err := sf.CountDown().Set(1); err != nil {
		panic(fmt.Errorf("ConvoyNet.attackConvoy(): при установке CountDown, err=\n\t%w", err))
	}
}

// Выполняет атаку на конвой
func (sf *Convoy) attack() (isNext bool) {
	// Вырезать ссылку на атаку
	strOut := ""
	lstConvoy := sf.GetLst()
	// <a href="convoy?13-2.ILinkListener-root-fightView-attackRegular" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>
	for _, strAttack := range lstConvoy {
		if strings.Contains(strAttack, `>ОБЫЧНЫЕ<`) {
			strOut = strAttack
			break
		}
		// Полная маскировка через 39:53
		if strings.Contains(strAttack, `Полная маскировка через `) {
			return false
		}
	}
	if strOut == "" { // Нечего атаковать
		return false
	}
	// Атакуем конвой
	lstLink := strings.Split(strOut, `<a href="`)
	if len(lstLink) != 2 {
		return false
	}
	strLink := lstLink[1]
	lstLink = strings.Split(strLink, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
	strLink = "http://wartank.ru/" + lstLink[0]
	{ // Выполнить атаку
		var err error
		lstConvoy, err = sf.net.Get(strLink)
		if err != nil {
			logrus.WithError(err).Error("ConvoyNet.attack(): in get page find attack")
			return false
		}
		if err = sf.Update(lstConvoy); err != nil {
			logrus.WithError(err).Error("Convoy.attack(): при обновлении lstConvoy")
			return false
		}
	}
	return true
}

// Забирает награду в конвое "Активируй боевую силу"
func (sf *Convoy) checkWarForce() {
	var (
		strOut    string
		isFind    bool
		lstConvoy = sf.GetLst()
		ind       int
	)
	if len(lstConvoy) == 0 {
		if err := sf.net.UpdateLst("Конвой"); err != nil {
			// log._rintf("Convoy.check6frage(): при обновлении пустого lstConvoy, err=\n\t%v\n", err)
			return
		}
		lstConvoy = sf.GetLst()
	}
	for ind, strOut = range lstConvoy {
		if strings.Contains(strOut, `Активируй усиление &quot;Боевая сила&quot;<br/>`) {
			isFind = true
			ind += 23
			strOut = lstConvoy[ind]
			break
		}
	}
	if !isFind {
		return
	}
	// <a class="simple-but border" href="convoy?70-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
	if !strings.Contains(strOut, `ILinkListener-missions-cc-0-c-awardLink`) {
		return
	}
	lstLink := strings.Split(strOut, `<a class="simple-but border" href="`)
	strLink := lstLink[1]
	lstLink = strings.Split(strLink, `"><span><span>Получить награду</span></span></a>`)
	// http://wartank.ru/convoy?80-1.ILinkListener-missions-cc-0-c-awardLink
	strLink = "http://wartank.ru/" + lstLink[0]
	lstConvoy, err := sf.net.Get(strLink)
	if err != nil {
		// log._rintf("ERRO Convoy.checkWarForce(): при выполнени команды GET, err=\n\t%v\n", err)
		return
	}
	if err := sf.Update(lstConvoy); err != nil {
		// log._rintf("ERRO Convoy.checkWarForce(): пр обновлении lstConvoy, err=\n\t%v\n", err)
		return
	}
	// log._rintf("INFO Convoy.checkWarForce(): награда получена\n")
}

// Забирает награду в конвое "Мастер дозора"
func (sf *Convoy) checkMaster() {
	var (
		strOut    string
		isFind    bool
		lstConvoy = sf.GetLst()
		ind       int
	)
	if len(lstConvoy) == 0 {
		if err := sf.net.UpdateLst("Конвой"); err != nil {
			// log._rintf("Convoy.check6frage(): при обновлении пустого lstConvoy, err=\n\t%v\n", err)
			return
		}
		lstConvoy = sf.GetLst()
	}
	for ind, strOut = range lstConvoy {
		if strings.Contains(strOut, `Проведи разведку в конвое<br/>`) {
			isFind = true
			ind += 23
			strOut = lstConvoy[ind]
			break
		}
	}
	if !isFind {
		return
	}
	// <a class="simple-but border" href="convoy?61-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
	if !strings.Contains(strOut, `ILinkListener-missions-cc-0-c-awardLink`) {
		return
	}
	lstLink := strings.Split(strOut, `<a class="simple-but border" href="`)
	strLink := lstLink[1]
	lstLink = strings.Split(strLink, `"><span><span>Получить награду</span></span></a>`)
	// http://wartank.ru/convoy?61-1.ILinkListener-missions-cc-0-c-awardLink
	strLink = "http://wartank.ru/" + lstLink[0]
	lstConvoy, err := sf.net.Get(strLink)
	if err != nil {
		// log._rintf("ERRO Convoy.checkMaster(): при выполнени команды GET, err=\n\t%v\n", err)
		return
	}
	if err := sf.Update(lstConvoy); err != nil {
		// log._rintf("ERRO Convoy.checkMaster(): пр обновлении lstConvoy, err=\n\t%v\n", err)
		return
	}
	// log._rintf("INFO Convoy.checkMaster(): награда получена\n")
}

// Забирает награду в конвое "Уничтожь 6 врагов в конвое"
func (sf *Convoy) check6frage() {
	var (
		strOut    string
		isFind    bool
		lstConvoy = sf.GetLst()
		ind       int
	)
	if len(lstConvoy) == 0 {
		if err := sf.net.UpdateLst("Конвой"); err != nil {
			// log._rintf("Convoy.check6frage(): при обновлении пустого lstConvoy, err=\n\t%v\n", err)
			return
		}
		lstConvoy = sf.GetLst()
	}
	for ind, strOut = range lstConvoy {
		if strings.Contains(strOut, `Уничтожь 6 врагов в конвое<br/>`) {
			isFind = true
			ind += 23
			strOut = lstConvoy[ind]
			break
		}
	}
	if !isFind {
		return
	}
	// <a class="simple-but border" href="convoy?61-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
	if !strings.Contains(strOut, `ILinkListener-missions-cc-0-c-awardLink`) {
		return
	}
	lstLink := strings.Split(strOut, `<a class="simple-but border" href="`)
	strLink := lstLink[1]
	lstLink = strings.Split(strLink, `"><span><span>Получить награду</span></span></a>`)
	// http://wartank.ru/convoy?61-1.ILinkListener-missions-cc-0-c-awardLink
	strLink = "http://wartank.ru/" + lstLink[0]
	lstConvoy, err := sf.net.Get(strLink)
	if err != nil {
		// log._rintf("ERRO Convoy.check6frage(): при выполнени команды GET, err=\n\t%v\n", err)
		return
	}
	if err := sf.Update(lstConvoy); err != nil {
		// log._rintf("ERRO Convoy.check6frage(): пр обновлении lstConvoy, err=\n\t%v\n", err)
		return
	}
	// log._rintf("INFO Convoy.check6frage(): награда получена\n")
}
