package angar

import (
	"fmt"
	"sync"

	"strconv"
	"strings"

	"wartank/pkg/components/section"
	"wartank/pkg/types"
	"wartank/server/serv_bots/warbot/angar/angar_attack"
	"wartank/server/serv_bots/warbot/angar/angarnet"
	"wartank/server/serv_bots/warbot/angar/base"
	"wartank/server/serv_bots/warbot/angar/batmas"
	"wartank/server/serv_bots/warbot/angar/battle"
	"wartank/server/serv_bots/warbot/angar/convoy"
	"wartank/server/serv_bots/warbot/angar/fuel"
	"wartank/server/serv_bots/warbot/angar/missions"
	"wartank/server/serv_bots/warbot/angar/netstat"
	"wartank/server/serv_bots/warbot/tank/tankstat/static_param"

	"github.com/sirupsen/logrus"
)

/*
	Объект ангара приложения
*/

// Ангар -- ангар для танка
type Ангар struct {
	*section.Section
	сеть       *angarnet.AngarNet
	топливоБой *angar_attack.ТопливоБой

	бот      types.ИБот
	конвой   *convoy.Convoy
	сражение *battle.Battle
	битва    *batmas.BatMas
	база     *base.База
	миссии   *missions.Миссии

	золото        types.ИСтатПарам
	уровень       types.ИСтатПарам
	progress      types.ИСтатПарам
	топливо       *fuel.Топливо
	игроковОнлайн types.ИСтатПарам

	сереброВсего types.ИСтатПарам
	silverOnline types.ИСтатПарам

	сетьСтат *netstat.NetStat
	блок     sync.Mutex
}

// НовАнгар -- возвращает новый *Angar
func НовАнгар(bot types.ИБот) (*Ангар, error) {
	section, err := section.NewSection(bot, "Ангар", `<title>Ангар</title>`)
	if err != nil {
		return nil, fmt.Errorf("НовАнгар(): in create ISection, err=\n\t%w", err)
	}

	золото, ош := static_param.НовСтатПарам("gold")
	if ош != nil {
		return nil, fmt.Errorf("НовАнгар(): при создании статистики золота, ош=\n\t%w", ош)
	}
	уровень, ош := static_param.НовСтатПарам("level")
	if ош != nil {
		return nil, fmt.Errorf("НовАнгар(): при создании статистики уровня бота, ош=\n\t%w", ош)
	}
	прогресс, ош := static_param.НовСтатПарам("progress")
	if ош != nil {
		return nil, fmt.Errorf("НовАнгар(): при создании статистики прогресса уровня, ош=\n\t%w", ош)
	}
	игроковОнлайн, ош := static_param.НовСтатПарам("online")
	if ош != nil {
		return nil, fmt.Errorf("НовАнгар(): при создании статистики игроков онлайн, ош=\n\t%w", ош)
	}
	сереброВсего, ош := static_param.НовСтатПарам("silver_all")
	if ош != nil {
		return nil, fmt.Errorf("НовАнгар(): при создании статистики серебра всего, ош=\n\t%w", ош)
	}
	сереброСессия, ош := static_param.НовСтатПарам("silver_online")
	if ош != nil {
		return nil, fmt.Errorf("НовАнгар(): при создании статистики серебра заработанного за сессию, ош=\n\t%w", ош)
	}
	сам := &Ангар{
		Section:       section,
		бот:           bot,
		золото:        золото,
		уровень:       уровень,
		progress:      прогресс,
		игроковОнлайн: игроковОнлайн,

		сереброВсего: сереброВсего,
		silverOnline: сереброСессия,
	}
	{ // Сеть
		сам.сеть, err = angarnet.NewAngarNet(сам)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in create <AngarNet>, err=\n\t%w", err)
		}
	}
	{ // Сеть атаки
		сам.топливоБой, err = angar_attack.НовТопливоБой(сам)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in create <AngarAttackNet>, err=\n\t%w", err)
		}
	}
	{ // Статистика
		сам.сетьСтат, err = netstat.NewNetStat(сам.бот)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in create NetResource, err=\n\t%w", err)
		}
	}
	{ // Convoy
		сам.конвой, err = convoy.NewConvoy(сам.бот)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in create IConvoy, err=\n\t%w", err)
		}
	}
	{ // Сражение
		сам.сражение, err = battle.NewBattle(сам.бот)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in create IBattle, err=\n\t%w", err)
		}
	}
	{ // Битва мастеров
		сам.битва, err = batmas.NewBatMas(сам.бот)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in create *BatMas, err=\n\t%w", err)
		}
		// go sf.batMas.Run()
	}
	{ // База
		сам.база, err = base.НовБаза(сам)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in make IBase, err=\n\t%w", err)
		}
	}
	{ // Миссии
		сам.миссии, err = missions.NewMissions(сам.бот)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in make *Missions, err=\n\t%w", err)
		}
	}
	{ // Топливо
		сам.топливо, err = fuel.НовТопливо(сам)
		if err != nil {
			return nil, fmt.Errorf("НовАнгар(): in make *Missions, err=\n\t%w", err)
		}
	}
	return сам, nil
}

// запускает обработку ангара
func (sf *Ангар) Пуск() error {
	{ // Запуск компонентов
		if err := sf.топливоБой.Пуск(); err != nil {
			return fmt.Errorf("Angar.Run(): при пуске сетевой секции опыт за топливо, err=\n\t%w", err)
		}
		if err := sf.конвой.Run(); err != nil {
			return fmt.Errorf("Angar.Run(): при пуске конвоя, err=\n\t%w", err)
		}
		if err := sf.сражение.Run(); err != nil {
			return fmt.Errorf("Angar.Run(): при пуске сражения, err=\n\t%w", err)
		}
		if err := sf.битва.Run(); err != nil {
			return fmt.Errorf("Angar.Run(): при пуске схватки, err=\n\t%w", err)
		}
		if err := sf.база.Run(); err != nil {
			return fmt.Errorf("Angar.Run(): при пуске базы, err=\n\t%w", err)
		}
		if err := sf.миссии.Пуск(); err != nil {
			return fmt.Errorf("Angar.Run(): при пуске наград, err=\n\t%w", err)
		}
		go sf.топливо.Run()
	}

	go func() {
		sf.SetCountDown(1)
		count := 0
		for {
			select {
			case <-sf.бот.Кнт().Done(): // Отмена контекста
				sf.ВремяОпрос().Стоп()
				return
			case <-sf.ВремяОпрос().КаналСиг(): // Метка времени
				sf.updateResurs()
				sf.сетьСтат.Update()
				switch count {
				case 0:
					sf.checkConvoy()
					count = 6
				default:
					count--
				}
				if err := sf.Section.SetCountDown(60); err != nil {
					sf.Закончить()
					logrus.WithError(err).Errorln("Angar.Run(): in update ICountTime")
					return
				}
			}
		}
	}()
	return nil
}

// Обновляет ресурсы ангара
func (sf *Ангар) updateResurs() {
	if err := sf.сеть.UpdateLst(); err != nil {
		// log._rintf("ERRO Angar.updateResurs(): при обработке ангара в сети, err=\n\t%v\n", err)
		return
	}
	sf.findGold()
	sf.findSilver()
}

// IMissions -- возвращает ссылку на объект миссий
func (sf *Ангар) Миссии() types.ИМиссии {
	return sf.миссии
}

// Сражение -- возвращает объект сражения
func (sf *Ангар) Сражение() types.ИСражениеСцена {
	return sf.сражение
}

// Конвой -- возвращает объект конвоя
func (sf *Ангар) Конвой() types.Конвой {
	return sf.конвой
}

// Золото -- возвращает объект золота
func (sf *Ангар) Золото() types.ИСтатПарам {
	return sf.золото
}

// Топливо -- возвращает объект топлива
func (sf *Ангар) Топливо() types.ИСтатПарам {
	return sf.топливо.Fuel()
}

// Уровень -- возвращает объект уровня игрока
func (sf *Ангар) Уровень() types.ИСтатПарам {
	return sf.уровень
}

// Прогресс -- возвращает прогрес уровня игрока
func (sf *Ангар) Прогресс() types.ИСтатПарам {
	return sf.progress
}

// ИгрокиОнлайн -- возвращает количество игроков онлайн
func (sf *Ангар) ИгрокиОнлайн() types.ИСтатПарам {
	return sf.игроковОнлайн
}

// СереброВсего -- возвращает объект всего серебра в ангаре
func (sf *Ангар) СереброВсего() types.ИСтатПарам {
	return sf.сереброВсего
}

// СереброЗаработаноСессия -- возвращает объект серебра за сессию в ангаре
func (sf *Ангар) СереброЗаработаноСессия() types.ИСтатПарам {
	return sf.silverOnline
}

// СереброОбновить -- на основе фактического серебра -- обновляет вырабатанное серебро
func (sf *Ангар) СереброОбновить(silverFact int) {
	if sf.сереброВсего.Получ() == 0 { // Если запуск
		sf.сереброВсего.Уст(silverFact)
		return
	}
	if sf.сереброВсего.Получ() > silverFact { // Если потрачего серебро
		sf.сереброВсего.Уст(silverFact)
		return
	}
	if sf.сереброВсего.Получ() < silverFact { // Если заработано
		so := sf.silverOnline.Получ()
		sd := silverFact - sf.сереброВсего.Получ()
		sf.silverOnline.Уст(so + sd)
	}
	sf.сереброВсего.Уст(silverFact)
}

// База -- возвращает базу
func (sf *Ангар) База() types.ИБаза {
	sf.блок.Lock()
	defer sf.блок.Unlock()
	return sf.база
}

// Проверяет на исполнение конвоя
func (sf *Ангар) checkConvoy() {
	var (
		strOut   string
		isFind   bool
		lstAngar = sf.СписПолучить()
	)
	for _, strOut = range lstAngar {
		if strings.Contains(strOut, `>Конвой</span>`) {
			isFind = true
			break
		}
	}
	if !isFind {
		return
	}
	if !strings.Contains(strOut, `<a class="simple-but border gray mb1" href="convoy"><span><span>Конвой</span></span></a>`) {
		return
	}
	sf.конвой.UpdateLst()
}

// Ищет в теле текста ангара серебро
func (sf *Ангар) findSilver() {
	// _mt.Println("\tAngarNet.findSilver()")
	lstAngar := sf.СписПолучить()
	var strOut string
	for _, strSilver := range lstAngar {
		if strings.Contains(strSilver, `<img title="Серебро" `) {
			strOut = strSilver
			break
		}
	}
	// Выделить топливо
	lstSilver := strings.Split(strOut, `<img title="Серебро" alt="Серебро" src="/images/icons/silver.png?2"/> `)
	strSilver := lstSilver[1]
	iSilver, err := strconv.Atoi(strSilver)
	if err != nil {
		// log._rintf("ERRO AngarNet.findSilver(): silver(%v) not number, err=\n\t%v\n", strSilver, err)
		return
	}
	sf.сереброВсего.Уст(iSilver)
}

// Ищет в теле текста ангара золото
func (sf *Ангар) findGold() {
	var (
		lstAngar = sf.бот.Ангар().СписПолучить()
		strOut   string
		isFind   bool
	)

	for _, strGold := range lstAngar {
		if strings.Contains(strGold, `<img title="Золото" `) {
			strOut = strGold
			isFind = true
			break
		}
	}
	if !isFind {
		return
	}
	// Выделить топливо
	lstGold := strings.Split(strOut, `<img title="Золото" alt="Золото" src="/images/icons/gold.png?2"/> `)
	strGold := lstGold[1]
	iGold, err := strconv.Atoi(strGold)
	if err != nil {
		// log._rintf("ERRO AngarNet.findGold(): gold(%v) not number, err=\n\t%v\n", strGold, err)
		return
	}
	sf.бот.Ангар().Золото().Уст(iGold)
}
