package localserver

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"path/filepath"
	"sync"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

var logger *log.Logger = new(log.Logger)
var dbLogger *BadgerLogger = &BadgerLogger{
	Logger: logger,
}

type BadgerLogger struct {
	*log.Logger
}

func (b *BadgerLogger) Debugf(message string, data ...interface{}) {
	b.Println(data...)
}

func (b *BadgerLogger) Errorf(message string, data ...interface{}) {
	b.Println(data...)
}

func (b *BadgerLogger) Infof(message string, data ...interface{}) {
	b.Println(data...)
}

func (b *BadgerLogger) Warningf(message string, data ...interface{}) {
	b.Println(data...)
}

type (
	// LocalServerHandlerMiddleware interface {
	// 	Process(ctx context.Context, req *http.Request, w http.ResponseWriter) (err error)
	// }

	SignalingClientManagerMiddleware interface {
		Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error)
	}

	// LocalServerHandler struct {
	// 	middlewares []LocalServerHandlerMiddleware
	// }

	LocalServer struct {
		ID                string
		GrpcClientManager *SignalingClientManager
	}

	SignalingClientManager struct {
		GrpcConn               grpc.ClientConnInterface
		SignalingManagerClient SignalingServiceClient
		GrpcLinkClient         SignalingService_LinkClient
		middlewares            []SignalingClientManagerMiddleware
	}

	CustomMenuItem struct {
		//MenuItem *systray.MenuItem
		ID       string
		State    bool
		SubMenus []*CustomMenuItem
		CallBack func(bool)
	}

	ReqType string

	LocalServerRequest struct {
		ReqType ReqType
		Payload map[string]interface{}
	}
)

var dataPath = ""

func NewLocalServer(grpcAddr uint, appDataPath, id, token string) (localServer *LocalServer, err error) {
	dataPath = appDataPath
	webRTCCallManager, err := NewWebRTCCallManager(id, token, NewWebrtcCallSoundManager(), NewWebrtcCallChatManager(), NewWebrtcCallVideoManager(), NewWebrtcCallFileManager())
	if err != nil {
		err = fmt.Errorf("error from call manager")
		return
	}
	zoneManager, err := NewZoneManager(id, token)
	if err != nil {
		fmt.Println(err)
		err = fmt.Errorf("error from zone manager")
		return
	}
	// webrtcFsManager, err := NewWebrtcFsManager(NewP2PFSDatachannelManager())
	// if err != nil {
	// 	err = fmt.Errorf("error from fs manager")
	// 	return
	// }
	webrtcGrpcMiddleware := NewWebRTCGrpcMiddleware(webRTCCallManager)
	ZoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager)
	signalingClientManager, err := NewGrpcClientManager(grpcAddr, id, webrtcGrpcMiddleware, ZoneGrpcMiddleware)
	if err != nil {
		return
	}
	webrtcGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
	webRTCCallManager.stream = signalingClientManager.GrpcLinkClient
	zoneManager.stream = signalingClientManager.GrpcLinkClient
	ZoneGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
	localServer = &LocalServer{
		ID:                id,
		GrpcClientManager: signalingClientManager,
	}
	return
}

func NewGrpcClientManager(addr uint, id string, middleware ...SignalingClientManagerMiddleware) (signalingClientManager *SignalingClientManager, err error) {
	conn, signalingClient, signalingLinkClient, err := NewGrpcConn(fmt.Sprintf("dev.zippytal.com:%d", addr), id)
	if err != nil {
		return
	}
	signalingClientManager = &SignalingClientManager{
		GrpcConn:               conn,
		SignalingManagerClient: signalingClient,
		GrpcLinkClient:         signalingLinkClient,
		middlewares:            middleware,
	}
	return
}

func NewGrpcConn(addr string, id string) (conn grpc.ClientConnInterface, signalingClient SignalingServiceClient, signalingLinkClient SignalingService_LinkClient, err error) {
	var cert = filepath.Join(certificatePath, "config", "fullchain.pem")
	creds, err := credentials.NewClientTLSFromFile(cert, "dev.zippytal.com")
	if err != nil {
		fmt.Println("there")
		return
	}
	var opts []grpc.DialOption = []grpc.DialOption{grpc.WithTransportCredentials(creds)}
	conn, err = grpc.Dial(addr, opts...)
	if err != nil {
		fmt.Println("over there")
		return
	}
	signalingClient = NewSignalingServiceClient(conn)
	signalingLinkClient, err = signalingClient.Link(context.Background())
	if err != nil {
		fmt.Println("really there")
		logger.Println(err)
		return
	}
	payload := map[string]any{
		"init": "init",
	}
	bs, err := json.Marshal(payload)
	if err != nil {
		logger.Println(err)
		return
	}
	if err = signalingLinkClient.Send(&SignalingMessage{
		Type:    "init",
		From:    id,
		To:      "server",
		Payload: bs,
	}); err != nil {
		return
	}
	return
}

// func NewLocalServerHandler(middlewares ...LocalServerHandlerMiddleware) (localServerHandler *LocalServerHandler) {
// 	localServerHandler = &LocalServerHandler{
// 		middlewares: middlewares,
// 	}
// 	return
// }

// func (lsh *LocalServerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// 	wg, done, errCh := &sync.WaitGroup{}, make(chan struct{}), make(chan error)
// 	for _, middleware := range lsh.middlewares {
// 		wg.Add(1)
// 		go func(m LocalServerHandlerMiddleware) {
// 			err := m.Process(req.Context(), req, w)
// 			if err != nil {
// 				errCh <- err
// 			}
// 			wg.Done()
// 		}(middleware)
// 	}
// 	go func() {
// 		wg.Wait()
// 		done <- struct{}{}
// 	}()
// 	select {
// 	case <-req.Context().Done():
// 	case <-done:
// 	case err := <-errCh:
// 		logger.Println(err)
// 	}
// }

func (gcm *SignalingClientManager) Handle(ctx context.Context) (err error) {
	done, errCh := make(chan struct{}), make(chan error)
	go func() {
		wg := new(sync.WaitGroup)
		for {
			res, err := gcm.GrpcLinkClient.Recv()
			if err != nil {
				errCh <- err
				return
			}
			for _, middleware := range gcm.middlewares {
				wg.Add(1)
				go func(m SignalingClientManagerMiddleware) {
					defer wg.Done()
					if err := m.Process(ctx, res, gcm.GrpcLinkClient); err != nil {
						logger.Println(err)
					}
				}(middleware)
			}
			wg.Wait()
		}
	}()
	select {
	case <-gcm.GrpcLinkClient.Context().Done():
		logger.Println("grpc context")
		err = gcm.GrpcLinkClient.Context().Err()
		return
	case <-ctx.Done():
		logger.Println("app context")
		err = ctx.Err()
		return
	case <-done:
		logger.Println("done")
		return
	case err = <-errCh:
		logger.Println("done with error")
		if closeErr := gcm.GrpcLinkClient.CloseSend(); closeErr != nil {
			return closeErr
		}
		return
	}
}
