package rest

import (
	"io"
	"time"

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

type (
	// RequestType - тип запроса
	RequestType byte
	// RequestFiles - словарь файлов запроса
	RequestFiles map[string]IReadCloserLen
	// TokenGenerateMethod - функция генерации токена авторизации
	TokenGenerateMethod func(data json.Map, expire int64) (string, error)
	// TokenParseMethod - функция для расшифровки токена
	TokenParseMethod func(token string) (json.Map, error)
)

const (
	RequestTypeIn RequestType = iota
	RequestTypeOut
	RequestTypeEvent
)

type IRequest interface {
	RType() RequestType
	RCommand() string
	RData() json.Map
	RFiles() RequestFiles
	RFile(string) (IReadCloserLen, bool)
	RClose()
}

////////////////////////////////////////////

type Request struct {
	Type    RequestType
	Command string
	Data    json.Map
	Files   RequestFiles
}

func (s *Request) RType() RequestType {
	return s.Type
}

func (s *Request) RCommand() string {
	return s.Command
}

func (s *Request) RData() json.Map {
	if s.Data == nil {
		return json.Map{}
	}
	return s.Data
}

func (s *Request) RFiles() RequestFiles {
	return s.Files
}

func (s *Request) RFile(name string) (IReadCloserLen, bool) {
	res, check := s.Files[name]
	return res, check
}

func (s *Request) RClose() {
	for _, file := range s.Files {
		file.Close()
	}
	s.Files = nil
}

func (s *Request) ParseOffset() int {
	return s.Data.Int32("offset", 0)
}

func (s *Request) ParseLimit() int {
	return s.Data.Int32("limit", 20)
}

////////////////////////////////////////////////////////////

type IRequestIn interface {
	IRequest
	Auth() json.Map
	SetAuth(json.Map)
	GenerateToken(data json.Map, expire int64) (string, error)
	ParseToken(token string) (json.Map, error)
	ROwner() IOwner
	RCore() any
	OutSuccess(data json.Map, files RequestFiles) IRequestOut
	OutError(err IErrorArgs) IRequestOut
	ClientData(key string) (any, bool)
	SetClientData(key string, data any)
	Fields() FieldList
}

type RequestIn struct {
	IRequest
	Owner          IOwner
	Core           any
	GeneratorToken TokenGenerateMethod
	ParserToken    TokenParseMethod
}

func (s *RequestIn) Auth() json.Map {
	if store, check := s.Owner.(IStream); check {
		return store.Auth()
	}
	return nil
}

func (s *RequestIn) SetAuth(auth json.Map) {
	if store, check := s.Owner.(IStream); check {
		store.SetAuth(auth)
	}
}

func (s *RequestIn) GenerateToken(data json.Map, expire int64) (string, error) {
	return s.GeneratorToken(data, expire)
}

func (s *RequestIn) ParseToken(token string) (json.Map, error) {
	return s.ParserToken(token)
}

func (s *RequestIn) ClientData(key string) (any, bool) {
	if store, check := s.Owner.(IStream); check {
		return store.ClientData(key)
	}
	return nil, false
}

func (s *RequestIn) SetClientData(key string, data any) {
	if store, check := s.Owner.(IStream); check {
		store.SetClientData(key, data)
	}
}

func (s *RequestIn) ROwner() IOwner {
	return s.Owner
}

func (s *RequestIn) RCore() any {
	return s.Core
}

func (s *RequestIn) OutSuccess(data json.Map, files RequestFiles) IRequestOut {
	return &RequestOut{
		Request: &Request{
			Type:    RequestTypeOut,
			Command: s.RCommand(),
			Data:    data,
			Files:   files,
		},
	}
}

func (s *RequestIn) OutError(err IErrorArgs) IRequestOut {
	return &RequestOut{
		Err: err,
	}
}

/////////////////////////////////////////////////////////

type IRequestOut interface {
	IRequest
	Write(io.Writer) IErrorArgs
}

/////////////////////////////////////////////////////////

type RequestOut struct {
	*Request
	Err IErrorArgs
}

func (s *RequestOut) Write(w io.Writer) IErrorArgs {
	return nil
}

//////////////////////////////////////////////////////

func NewRequestStreamEvent(command string, data json.Map, files RequestFiles) IRequestOut {
	return &RequestStream{
		Timeout: time.Now().Add(time.Minute),
		Request: &Request{
			Type:    RequestTypeEvent,
			Command: command,
			Data:    data,
			Files:   files,
		},
	}
}

func NewRequestStreamMessage(command string, data json.Map, files RequestFiles) IRequestOut {
	return &RequestStream{
		Timeout: time.Now().Add(time.Minute),
		Request: &Request{
			Type:    RequestTypeIn,
			Command: command,
			Data:    data,
			Files:   files,
		},
	}
}