package rest_websocket

import (
	"context"
	"log"
	"time"

	"git.ali33.ru/fcg-xvii/go-tools/json"
	"git.ali33.ru/fcg-xvii/rest"
)

func NewSocketServer(socket *Socket, appConf *appConfig) *SocketServer {
	res := &SocketServer{
		socket:     socket,
		appConf:    appConf,
		auth:       json.Map{},
		chIn:       make(chan rest.IRequestIn, 1),
		clientData: make(map[string]any),
	}
	go res.work()
	return res
}

type SocketServer struct {
	socket     *Socket
	appConf    *appConfig
	auth       json.Map
	chIn       chan rest.IRequestIn
	clientData json.Map
}

func (s *SocketServer) ClientData(key string) (any, bool) {
	res, check := s.clientData[key]
	return res, check
}

func (s *SocketServer) SetClientData(key string, data any) {
	s.clientData[key] = data
}

func (s *SocketServer) Auth() json.Map {
	return s.auth
}

func (s *SocketServer) SetAuth(auth json.Map) {
	s.auth = auth
}

func (s *SocketServer) In() <-chan rest.IRequestIn {
	return s.chIn
}

func (s *SocketServer) IsStream() bool {
	return true
}

func (s *SocketServer) Context() context.Context {
	return s.socket.Context()
}

func (s *SocketServer) Close() {
	s.socket.Close()
}

func (s *SocketServer) SendMessage(req rest.IRequestOut) (<-chan *rest.RequestStream, error) {
	var request *rest.RequestStream
	if rreq, check := req.(*rest.RequestStream); check {
		request = rreq
	} else {
		request = &rest.RequestStream{
			Request: &rest.Request{
				Type:    req.RType(),
				Command: req.RCommand(),
				Data:    req.RData(),
				Files:   req.RFiles(),
			},
			Timeout: time.Now().Add(time.Second * 10),
		}
	}
	return s.socket.SendMessage(request)
}

func (s *SocketServer) work() {
loop:
	for {
		select {
		case <-s.socket.Context().Done():
			return
		case req := <-s.socket.MessagesIn():
			reqIn := &RequestIn{
				RequestStream: req,
				owner:         s,
				core:          s.appConf.core,
			}
			var reqOut rest.IRequestOut
			command, check := s.appConf.app.Executer(reqIn)
			if !check {
				reqOut = reqIn.OutError(rest.ErrorMessage("ErrNotFound", "command is not found"))
			} else {
				// serialize
				if err := rest.Serialize(reqIn.RData(), command); err != nil {
					s.socket.Close()
					return
				}
				// validate
				if validator, check := command.(rest.IValidator); check {
					reqOut = validator.Validate(reqIn)
					if reqOut != nil {
						if _, err := s.SendMessage(reqOut); err != nil {
							log.Println("socket send error", err)
						}
						reqOut.RClose()
						continue loop
					}
				}
				reqOut = command.Execute(reqIn)
			}
			s.SendMessage(reqOut)
			reqOut.RClose()
			//s.chIn <- reqIn
		}
	}
}