package model

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"golang.org/x/crypto/ed25519"
	"io"
	"sync"
)

// Authorization is a type determining client assigned authorization to a peer
// Deprecated - Only used for Importing legacy profile formats
// Still used in some APIs in UI but will be replaced prior to full deprecation
type Authorization string

const (
	// AuthUnknown is an initial state for a new unseen peer
	AuthUnknown Authorization = "unknown"
	// AuthApproved means the client has approved the peer, it can send messages to us, perform GetVals, etc
	AuthApproved Authorization = "approved"
	// AuthBlocked means the client has blocked the peer, it's messages and connections should be rejected
	AuthBlocked Authorization = "blocked"
)

// PublicProfile is a local copy of a CwtchIdentity
// Deprecated - Only used for Importing legacy profile formats
type PublicProfile struct {
	Name                   string
	Ed25519PublicKey       ed25519.PublicKey
	Authorization          Authorization
	DeprecatedBlocked      bool `json:"Blocked"`
	Onion                  string
	Attributes             map[string]string
	Timeline               Timeline `json:"-"`
	LocalID                string   // used by storage engine
	State                  string   `json:"-"`
	lock                   sync.Mutex
	UnacknowledgedMessages map[string]int
}

// Profile encapsulates all the attributes necessary to be a Cwtch Peer.
// Deprecated - Only used for Importing legacy profile formats
type Profile struct {
	PublicProfile
	Contacts          map[string]*PublicProfile
	Ed25519PrivateKey ed25519.PrivateKey
	Groups            map[string]*Group
}

// MaxGroupMessageLength is the maximum length of a message posted to a server group.
// TODO: Should this be per server?
const MaxGroupMessageLength = 1800

func getRandomness(arr *[]byte) {
	if _, err := io.ReadFull(rand.Reader, (*arr)[:]); err != nil {
		// If we can't do randomness, just crash something is very very wrong and we are not going
		// to resolve it here....
		panic(err.Error())
	}
}

// GenerateRandomID generates a random 16 byte hex id code
func GenerateRandomID() string {
	randBytes := make([]byte, 16)
	rand.Read(randBytes)
	return hex.EncodeToString(randBytes)
}

// GetCopy returns a full deep copy of the Profile struct and its members (timeline inclusion control by arg)
func (p *Profile) GetCopy(timeline bool) *Profile {
	p.lock.Lock()
	defer p.lock.Unlock()

	newp := new(Profile)
	bytes, _ := json.Marshal(p)
	json.Unmarshal(bytes, &newp)

	if timeline {
		for groupID := range newp.Groups {
			if group, exists := newp.Groups[groupID]; exists {
				if pGroup, exists := p.Groups[groupID]; exists {
					group.Timeline = *(pGroup).Timeline.GetCopy()
				}
			}
		}

		for peerID := range newp.Contacts {
			if peer, exists := newp.Contacts[peerID]; exists {
				if pPeer, exists := p.Contacts[peerID]; exists {
					peer.Timeline = *(pPeer).Timeline.GetCopy()
				}
			}
		}
	}

	return newp
}
