package client

import (
	"context"
	"fmt"
	"io"
	"net/netip"
	"strings"

	"github.com/netbirdio/netbird/shared/signal/proto"
	"github.com/netbirdio/netbird/version"

	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

// A set of tools to exchange connection details (Wireguard endpoints) with the remote peer.

const (
	StreamConnected    Status = "Connected"
	StreamDisconnected Status = "Disconnected"

	// DirectCheck indicates support to direct mode checks
	DirectCheck uint32 = 1
)

// Status is the status of the client
type Status string

type Client interface {
	io.Closer
	StreamConnected() bool
	GetStatus() Status
	Receive(ctx context.Context, msgHandler func(msg *proto.Message) error) error
	Ready() bool
	IsHealthy() bool
	WaitStreamConnected(context.Context)
	SendToStream(msg *proto.EncryptedMessage) error
	Send(msg *proto.Message) error
	SetOnReconnectedListener(func())
}

// Credential is an instance of a GrpcClient's Credential
type Credential struct {
	UFrag string
	Pwd   string
}

// CredentialPayload bundles the fields of a signal Body for MarshalCredential.
type CredentialPayload struct {
	Type            proto.Body_Type
	WgListenPort    int
	Credential      *Credential
	RosenpassPubKey []byte
	RosenpassAddr   string
	RelaySrvAddress string
	RelaySrvIP      netip.Addr
	SessionID       []byte
}

// UnMarshalCredential parses the credentials from the message and returns a Credential instance
func UnMarshalCredential(msg *proto.Message) (*Credential, error) {

	credential := strings.Split(msg.GetBody().GetPayload(), ":")
	if len(credential) != 2 {
		return nil, fmt.Errorf("error parsing message body %s", msg.Body)
	}
	return &Credential{
		UFrag: credential[0],
		Pwd:   credential[1],
	}, nil
}

// MarshalCredential marshal a Credential instance and returns a Message object
func MarshalCredential(myKey wgtypes.Key, remoteKey string, p CredentialPayload) (*proto.Message, error) {
	body := &proto.Body{
		Type:           p.Type,
		Payload:        fmt.Sprintf("%s:%s", p.Credential.UFrag, p.Credential.Pwd),
		WgListenPort:   uint32(p.WgListenPort),
		NetBirdVersion: version.NetbirdVersion(),
		RosenpassConfig: &proto.RosenpassConfig{
			RosenpassPubKey:     p.RosenpassPubKey,
			RosenpassServerAddr: p.RosenpassAddr,
		},
		SessionId: p.SessionID,
	}
	if p.RelaySrvAddress != "" {
		body.RelayServerAddress = &p.RelaySrvAddress
	}
	if p.RelaySrvIP.IsValid() {
		body.RelayServerIP = p.RelaySrvIP.Unmap().AsSlice()
	}
	return &proto.Message{
		Key:       myKey.PublicKey().String(),
		RemoteKey: remoteKey,
		Body:      body,
	}, nil
}
