// package warbot -- бот для игры в wartank
package warbot

import (
	"context"
	"fmt"
	"log"
	"strings"

	"wartank/pkg/components/safebool"
	"wartank/pkg/types"
	"wartank/server/serv_bots/warbot/angar"
	"wartank/server/serv_bots/warbot/tank"
	"wartank/server/serv_bots/warbot/warbot_config"
	"wartank/server/serv_bots/warbot/warbot_net"
)

// ВарБот -- бот для игры в вартанк
type ВарБот struct {
	сервер         types.ИСервер
	store          types.IStore
	танк           *tank.Танк
	errFinal       error // Финальная ошибка работы, если была
	ангар          types.ИАнгар
	сеть           types.ИБотСеть
	еслиРаботает   *safebool.SafeBool
	еслиАвтозапуск *safebool.SafeBool
	конфиг         *warbot_config.ВарБотКонфиг // Конфиг бота для хранения в базе
	кнт            context.Context             // Контекст бота
	фтОтмена       func()                      // Функция отменя контекста бота
}

// ЗагрузитьВарБот -- загружает бота из базы
func ЗагрузитьВарБот(сервер types.ИСервер, логин string) (*ВарБот, error) {
	{ // Предусловия
		if сервер == nil {
			return nil, fmt.Errorf("ЗагрузитьВарБот(): IApp is nil")
		}
		if логин == "" {
			return nil, fmt.Errorf("ЗагрузитьВарБот(): name is empty")
		}
	}
	log.Printf("ЗагрузитьВарБот(): name=%q\n", логин)
	store := сервер.Store()
	strData, err := store.Get("/bots/" + логин)
	if err != nil {
		if !strings.Contains(err.Error(), "not found") {
			return nil, fmt.Errorf("ЗагрузитьВарБот(): in load bot %q from store, err=\n\t%w", логин, err)
		}
		return nil, fmt.Errorf("ЗагрузитьВарБот(): bot %q not found in store", логин)
	}
	конфиг := &warbot_config.ВарБотКонфиг{}
	if err = конфиг.Unmarshall(strData); err != nil {
		return nil, fmt.Errorf("ЗагрузитьВарБот(): in unmarshall WarBotConfig(%q) from store, err=\n\t%w", логин, err)
	}
	sf, err := создатьЯдроВарБот(сервер, конфиг)
	if err != nil {
		return nil, fmt.Errorf("ЗагрузитьВарБот(): in make core for bot %q, err=\n\t%w", логин, err)
	}
	return sf, nil

}

// НовВарБот -- возвращает новый WarBot
func НовВарБот(сервер types.ИСервер, логин, пароль string) (*ВарБот, error) {
	{ // Предусловия
		if сервер == nil {
			return nil, fmt.Errorf("НовВарБот(): IApp is nil")
		}
		if логин == "" {
			return nil, fmt.Errorf("НовВарБот(): login is empty")
		}
		if пароль == "" {
			return nil, fmt.Errorf("НовВарБот(): pass is empty")
		}
	}
	log.Printf("НовВарБот(): name=%q\n", логин)
	config := &warbot_config.ВарБотКонфиг{
		ЕслиАвтозапуск_: false,
		Логин_:          логин,
		Пароль_:         пароль,
	}
	sf, err := создатьЯдроВарБот(сервер, config)
	if err != nil {
		return nil, fmt.Errorf("НовВарБот(): in make core bot %q, err=\n\t%w", логин, err)
	}
	if err := sf.save(); err != nil {
		return nil, fmt.Errorf("НовВарБот(): in self save to store, err=\n\t%w", err)
	}
	return sf, nil
}

// Создаёт ядро бота
func создатьЯдроВарБот(server types.ИСервер, config *warbot_config.ВарБотКонфиг) (*ВарБот, error) {
	{ // Предусловия
		if server == nil {
			return nil, fmt.Errorf("NewWarBot(): IServer==nil")
		}
		if config == nil {
			return nil, fmt.Errorf("NewWarBot(): WarBotConfig==nil")
		}
	}
	танк, ош := tank.NewTank()
	if ош != nil {
		return nil, fmt.Errorf("NewWarBot(): при создании параметров танка, ош=\n\t%w", ош)
	}
	ctx, fnCancel := context.WithCancel(server.CtxApp())
	sf := &ВарБот{
		сервер:         server,
		store:          server.Store(),
		танк:           танк,
		еслиРаботает:   safebool.NewSafeBool(),
		еслиАвтозапуск: safebool.NewSafeBool(),
		конфиг:         config,
		кнт:            ctx,
		фтОтмена:       fnCancel,
	}
	var err error
	{ // WarBotNet
		sf.сеть, err = warbot_net.NewWarBotNet(sf)
		if err != nil {
			return nil, fmt.Errorf("NewWarBot(): in make IBotNet, err=\n\t%w", err)
		}
	}
	sf.ангар, err = angar.НовАнгар(sf)
	if err != nil {
		return nil, fmt.Errorf("NewWarBot(): bot(%q) in make IAngar, err=\n\t%w", sf.Имя(), err)
	}
	if sf.конфиг.ЕслиАвтозапуск_ {
		sf.еслиАвтозапуск.Уст()
		sf.Пуск()
	}
	return sf, nil
}

// Сервер -- возвращает ссылку на объект сервера
func (sf *ВарБот) Сервер() types.ИСервер {
	return sf.сервер
}

// ЕслиПуск -- возвращает признак, что бот подключен
func (sf *ВарБот) ЕслиПуск() bool {
	return sf.еслиРаботает.Получ()
}

// Имя -- возвращает имя бота
func (sf *ВарБот) Имя() string {
	return sf.конфиг.Логин_
}

// Пароль -- возвращает пароль бота
func (sf *ВарБот) Пароль() string {
	return sf.конфиг.Пароль_
}

// Пуск -- запускает бот в работу
func (sf *ВарБот) Пуск() (err error) {
	if sf.еслиРаботает.Получ() {
		return nil
	}
	if err := sf.ангар.Пуск(); err != nil {
		return fmt.Errorf("WarBot.Run(): bot(%q) in run angar, err=\n\t%w", sf.Имя(), err)
	}
	sf.еслиРаботает.Уст()
	return nil
}

// Error -- возвращает финальную ошибку работы, если была
func (sf *ВарБот) Error() error {
	return sf.errFinal
}

// Ангар -- возвращает ангар игры
func (sf *ВарБот) Ангар() types.ИАнгар {
	return sf.ангар
}

// Танк -- возвращает объект танка
func (sf *ВарБот) Танк() types.ИТанк {
	return sf.танк
}

// Сеть -- возвращает ссылку на свой сетевой клиент
func (sf *ВарБот) Сеть() types.ИБотСеть {
	return sf.сеть
}

// АвтоИграЕсли -- возвращает признак автоматичского запуска бота
func (sf *ВарБот) АвтоИграЕсли() bool {
	return sf.еслиАвтозапуск.Получ()
}

// АвтоИграУст -- устанавливает признак автоматического запуска бота
func (sf *ВарБот) АвтоИграУст() {
	log.Printf("WarBot.SetAutoGame()")
	sf.еслиАвтозапуск.Уст()
	sf.конфиг.ЕслиАвтозапуск_ = true
	sf.saveConfig()
}

// АвтоИграСброс -- сбрасывает признак автоматического запуска бота
func (sf *ВарБот) АвтоИграСброс() {
	log.Printf("WarBot.ResetAutoGame()")
	sf.еслиАвтозапуск.Уст()
	sf.конфиг.ЕслиАвтозапуск_ = false
	sf.saveConfig()
}

// Сохраняет конфиг бота
func (sf *ВарБот) saveConfig() {
	log.Printf("WarBot.saveConfig()")
	strConf := sf.конфиг.Marshall()
	err := sf.store.Put("/bots/"+sf.Имя(), strConf)
	if err != nil {
		log.Printf("WarBot.saveConfig(): err=\n\t%v\n", err)
	}
}

// Кнт -- возвращает контекст бота
func (sf *ВарБот) Кнт() context.Context {
	return sf.кнт
}

// Закончить -- отменяет контекст бота
func (sf *ВарБот) Закончить() {
	sf.фтОтмена()
}

// Сохраняет себя в базу
func (sf *ВарБот) save() error {
	err := sf.store.Put("/bots/"+sf.Имя(), sf.конфиг.Marshall())
	if err != nil {
		return fmt.Errorf("WarBot.save(): in self save to store bot(%q), err=\n\t%w", sf.Имя(), err)
	}
	return nil
}
