package trace

import (
	"github.com/adamweixuan/getty"
	"go.uber.org/zap"
	"time"
	"virjar.com/majora-go/log"
)

var (
	sessionEventChan  = make(chan *sessionEvent, 100)
	ConnectEvent      = "ConnectEvent"
	TransferEvent     = "TransferEvent"
	MajoraSessionName = "MajoraSessionId"
	UpStreamEvent     = "UpStream"
	DisconnectEvent   = "Disconnect"
	sessionIdNop      = "session_id_not_set"
)

func init() {
	go func() {
		for {
			e := <-sessionEventChan
			if e.Err != nil {
				log.Trace().Error("TraceError", zap.String("sessionId", e.sessionId),
					zap.Any("event", e.Event), zap.Error(e.Err))
			} else {
				log.Trace().Info("Trace", zap.String("sessionId", e.sessionId),
					zap.Any("event", e.Event))
			}
		}
	}()
}

// Event 事件
type Event struct {
	// 发生时间
	Timestamp time.Time

	// 事件名称
	EventName string

	// 事件消息
	Message string

	// 错误，如果存在
	Err error
}

type sessionEvent struct {
	sessionId string
	*Event
}

type Recorder interface {
	RecordEvent(eventName string, message string)

	RecordErrorEvent(eventName string, message string, err error)

	Enable() bool
}

type NopRecorder struct{}

func (n *NopRecorder) RecordEvent(eventName string, message string) {

}

func (n *NopRecorder) RecordErrorEvent(eventName string, message string, err error) {

}

func (n *NopRecorder) Enable() bool {
	return false
}

type RecorderImpl struct {
	sessionId string
}

func NewRecorderImpl(sessionId string) *RecorderImpl {
	return &RecorderImpl{
		sessionId: sessionId,
	}

}

func (r *RecorderImpl) RecordEvent(eventName string, message string) {
	r.RecordErrorEvent(eventName, message, nil)
}

func (r *RecorderImpl) RecordErrorEvent(eventName string, message string, err error) {
	event := &Event{
		Timestamp: time.Now(),
		EventName: eventName,
		Message:   message,
		Err:       err,
	}
	sessionEvent := &sessionEvent{
		sessionId: r.sessionId,
		Event:     event,
	}

	// 当 trace 日志 channel 超过 90% 时放弃 trace 记录，防止阻塞主业务
	sessionChanCap := cap(sessionEventChan)
	sessionChanLen := len(sessionEventChan)
	if sessionChanLen > sessionChanCap*9/10 {
		log.Run().Errorf("sessionEventChan data to many -> cap:%d len:%d", sessionChanCap, sessionChanLen)
		return
	}
	sessionEventChan <- sessionEvent
}

func (r *RecorderImpl) Enable() bool {
	return true
}

var defaultNopRecorder = NopRecorder{}

func AcquireRecorder(sessionId string) Recorder {
	return &RecorderImpl{
		sessionId: sessionId,
	}
}

type Session struct {
	Recorder Recorder
}

func NewSession(sessionId string) *Session {
	if len(sessionId) == 0 {
		sessionId = sessionIdNop
	}
	return &Session{
		Recorder: AcquireRecorder(sessionId),
	}
}

func GetTraceRecorderFromSession(session getty.Session) Recorder {
	majoraSession := session.GetAttribute(MajoraSessionName)
	traceSession, ok := majoraSession.(*Session)
	if !ok {
		return NewSession(sessionIdNop).Recorder
	}
	return traceSession.Recorder
}
