// package web_api -- реализация API запросов в веб-сервер
package web_api

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/websocket/v2"

	"wartank/pkg/types"
)

// WebApi -- рализация веб-запросов
type WebApi struct {
	server types.IServer
	store  types.IStore
}

// NewWebApi -- возвращает новый WebApi
func NewWebApi(server types.IServer) (*WebApi, error) {
	if server == nil {
		return nil, fmt.Errorf("NewWebApi(): IServer is nil")
	}

	sf := &WebApi{
		server: server,
		store:  server.Store(),
	}
	server.ServWeb().Router().Use("/api/ws", sf.fnWs)
	server.ServWeb().Router().Get("/api/ws", websocket.New(sf.fnWsRun))

	return sf, nil
}

// Run -- заглушка к вызову
func (sf *WebApi) Run() {}

// fnWs -- апгрейдит веб-сокет
func (sf *WebApi) fnWs(c *fiber.Ctx) error {
	if websocket.IsWebSocketUpgrade(c) {
		c.Locals("allowed", true)
		return c.Next()
	}
	return fiber.ErrUpgradeRequired
}

// fnWsRun -- запускает веб-сокет в работу
func (sf *WebApi) fnWsRun(c *websocket.Conn) {
	// c.Locals is added to the *websocket.Conn
	log.Println(c.Locals("allowed"))  // true
	log.Println(c.Params("id"))       // 123
	log.Println(c.Query("v"))         // 1.0
	log.Println(c.Cookies("session")) // ""

	// websocket.Conn bindings https://pkg.go.dev/github.com/fasthttp/websocket?tab=doc#pkg-index
	var (
		mt       int
		dictReq  = make(map[string]string)
		dictResp = make(map[string]string)
		binReq   []byte
		err      error
		block    sync.Mutex
	)
	fnWrite := func() (isOk bool) {
		defer block.Unlock()
		binResp, err := json.Marshal(dictResp)
		if err != nil {
			log.Println("WebApi.fnWsRun(): in marshall response, err=", err)
			return false
		}
		if err = c.WriteMessage(mt, binResp); err != nil {
			log.Println("WebApi.fnWsRun(): in write, err=", err)
			return false
		}
		return true
	}
	for {
		block.Lock()
		if mt, binReq, err = c.ReadMessage(); err != nil {
			log.Println("WebApi.fnWsRun(): in read, err=", err)
			return
		}
		dictResp = make(map[string]string)
		dictResp["err"] = ""
		err = json.Unmarshal(binReq, &dictReq)
		if err != nil {
			dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in unmarshall request, err=\n\t%v", err)
			if !fnWrite() {
				return
			}
			continue
		}
		topic, isOk := dictReq["topic"]
		if !isOk {
			dictResp["err"] = "WebApi.fnWsRun(): dictReq not have key `topic`"
			if !fnWrite() {
				return
			}
			continue
		}
		switch topic {
		case "/server/time": // Запрос времени сервера
			dictResp["/server/time"] = time.Now().UTC().Format("2006-01-02 15:04:05.000")
		case "/root/password/is_exists":
			_, err := sf.store.Get("/root/password/val")
			if err != nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in get root password, err=\n\t%v", err)
			} else {
				dictResp["/root/password/is_exists"] = "true"
			}
		case "/root/password/set":
			strPass := dictReq["pass"]
			err := sf.store.Put("/root/password/val", strPass)
			if err != nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in save root password, err=\n\t%v", err)
			}
		case "/root/password/check": // Проверка пароля рута
			binPass, err_ := sf.store.Get("/root/password/val")
			if err_ != nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in get root password, err=\n\t%v", err_)
			} else {
				strPassRoot := string(binPass)
				strPassUser := dictReq["pass"]
				if strPassRoot != strPassUser {
					dictResp["err"] = "WebApi.fnWsRun(): bad root password"
				}
			}
		case "/bot/list/load": // Запрос списка ботов
			binBots, err := sf.store.Get("/bot/list")
			if err != nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in get list bot from store, err=\n\t%v\n", err)
			} else {
				dictResp["/bot/list"] = string(binBots)
			}
		case "/bot/list/save": // Сохранение списка ботов
			strBotList := dictReq["binData"]
			err := sf.store.Put("/bot/list", strBotList)
			if err != nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in save list bot to store, err=\n\t%v\n", err)
			}
		case "/store/put": // Поместить запись в хранилище
			key := dictReq["key"]
			val := dictReq["val"]
			err := sf.store.Put(key, val)
			if err != nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): in put key(%q) to store, err=\n\t%v\n", key, err)
			}
		case "/bot/status": // Получить статус бота
			name := dictReq["name"]
			bot := sf.server.ServBots().Get(name)
			if bot == nil {
				dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): unknown bit name (%q)", name)
			} else {
				dictResp["/bot/pass"] = bot.Pass()
				dictResp["/bot/name"] = bot.Name()
				dictResp["/bot/online"] = fmt.Sprint(bot.BotNet().IsOnline().Get())
				dictResp["/bot/fuel"] = fmt.Sprint(bot.Angar().Fuel().Get())
				dictResp["/bot/gold"] = fmt.Sprint(bot.Angar().Gold().Get())
				angar := bot.Angar()
				base := angar.Base()
				bank := base.Bank()
				silverBot := bank.SilverBot()
				dictResp["/bot/silver"] = fmt.Sprint(silverBot.Get())
				dictResp["/bank/silver-time"] = bot.Angar().Base().Bank().CountDown().String()
				dictResp["/bank/silver-mode"] = bot.Angar().Base().Bank().ModeCurrent().Work()
				dictResp["/angar/silver-all"] = fmt.Sprint(bot.Angar().SilverAll().Get())
				dictResp["/bot/mine-time"] = bot.Angar().Base().Mine().CountDown().String()
				dictResp["/bot/mine-mode"] = bot.Angar().Base().Mine().ModeCurrent().Work()
				dictResp["/mine/ruda"] = fmt.Sprint(bot.Angar().Base().Mine().Ruda().Get())
			}
		default:
			dictReq = make(map[string]string)
			dictResp["err"] = fmt.Sprintf("WebApi.fnWsRun(): unknown topic (%q)", topic)
		}
		if !fnWrite() {
			return
		}
	}
}
