package peer

import (
	"cwtch.im/cwtch/event"
	"cwtch.im/cwtch/model"
	"cwtch.im/cwtch/model/attr"
	"cwtch.im/cwtch/protocol/connections"
	"cwtch.im/cwtch/settings"
	"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
	"git.openprivacy.ca/openprivacy/connectivity"
)

// AccessPeeringState provides access to functions relating to the underlying connections of a peer.
type AccessPeeringState interface {
	GetPeerState(string) connections.ConnectionState
}

// ModifyPeeringState is a meta-interface intended to restrict callers to modify-only access to connection peers
type ModifyPeeringState interface {
	BlockUnknownConnections()
	AllowUnknownConnections()
	PeerWithOnion(string)
	QueueJoinServer(string)
	DisconnectFromPeer(string)
	DisconnectFromServer(string)
}

// ModifyContactsAndPeers is a meta-interface intended to restrict a call to reading and modifying contacts
// and peers.
type ModifyContactsAndPeers interface {
	ModifyPeeringState
}

// ReadServers provides access to the servers
type ReadServers interface {
	GetServers() []string
}

// ModifyGroups provides write-only access add/edit/remove new groups
type ModifyGroups interface {
	ImportGroup(string) (int, error)
	StartGroup(string, string) (int, error)
}

// ModifyServers provides write-only access to servers
type ModifyServers interface {
	AddServer(string) (string, error)
	ResyncServer(onion string) error
}

// SendMessages enables a caller to sender messages to a contact
type SendMessages interface {
	// SendMessage sends a raw message to the conversation.
	// SendMessage is a deprecated public API. Use EnhancedSendMessage instead
	SendMessage(conversation int, message string) (int, error)

	// EnhancedSendMessage Attempts to Send a Message and Immediately Attempts to Lookup the Message in the Database
	EnhancedSendMessage(conversation int, message string) string

	SendInviteToConversation(conversationID int, inviteConversationID int) (int, error)

	// EnhancedSendInviteMessage Attempts to Send an Invite and Immediately Attempts to Lookup the Message in the Database
	EnhancedSendInviteMessage(conversation int, inviteConversationID int) string

	SendScopedZonedGetValToContact(conversationID int, scope attr.Scope, zone attr.Zone, key string)
}

// CwtchPeer provides us with a way of testing systems built on top of cwtch without having to
// directly implement a cwtchPeer.
type CwtchPeer interface {

	// Core Cwtch Peer Functions that should not be exposed to
	// most functions
	Init(event.Manager)

	GenerateProtocolEngine(acn connectivity.ACN, bus event.Manager, engineHooks connections.EngineHooks) (connections.Engine, error)

	AutoHandleEvents(events []event.Type)
	Listen()
	StartConnections(doPeers, doServers bool)
	// Deprecated in 1.10
	StartPeersConnections()
	// Deprecated in 1.10
	StartServerConnections()

	Shutdown()

	// GetOnion is deprecated. If you find yourself needing to rely on this method it is time
	// to consider replacing this with a GetAddress(es) function that can fully expand cwtch beyond the boundaries
	// of tor v3 onion services.
	// Deprecated
	GetOnion() string

	// SetScopedZonedAttribute allows the setting of an attribute by scope and zone
	// scope.zone.key = value
	SetScopedZonedAttribute(scope attr.Scope, zone attr.Zone, key string, value string)

	// GetScopedZonedAttribute allows the retrieval of an attribute by scope and zone
	// scope.zone.key = value
	GetScopedZonedAttribute(scope attr.Scope, zone attr.Zone, key string) (string, bool)

	// GetScopedZonedAttributeKeys returns all keys associated with a given scope and zone
	GetScopedZonedAttributeKeys(scope attr.Scope, zone attr.Zone) ([]string, error)

	AccessPeeringState
	ModifyPeeringState

	ModifyGroups

	ReadServers
	ModifyServers

	SendMessages

	// Import Bundle
	ImportBundle(string) error
	EnhancedImportBundle(string) string

	// New Unified Conversation Interfaces
	NewConversation(handle string, acl model.AccessControlList) (int, error)
	InitChannel(conversation int, channel int) error
	NewContactConversation(handle string, acl model.AccessControl, accepted bool) (int, error)
	FetchConversations() ([]*model.Conversation, error)
	ArchiveConversation(conversation int)
	GetConversationInfo(conversation int) (*model.Conversation, error)
	FetchConversationInfo(handle string) (*model.Conversation, error)

	// API-level management of conversation access control
	UpdateConversationAccessControlList(id int, acl model.AccessControlList) error
	EnhancedUpdateConversationAccessControlList(conversation int, acjson string) error

	GetConversationAccessControlList(conversation int) (model.AccessControlList, error)
	EnhancedGetConversationAccessControlList(conversation int) (string, error)

	// Convieniance Functions for ACL Management
	AcceptConversation(conversation int) error
	BlockConversation(conversation int) error
	UnblockConversation(conversation int) error

	SetConversationAttribute(conversation int, path attr.ScopedZonedPath, value string) error
	GetConversationAttribute(conversation int, path attr.ScopedZonedPath) (string, error)
	SetConversationAttributeInt(conversation int, path attr.ScopedZonedPath, value int) error
	GetConversationAttributeInt(conversation int, path attr.ScopedZonedPath) (int, error)
	DeleteConversation(conversation int) error

	// New Unified Conversation Channel Interfaces
	GetChannelMessage(conversation int, channel int, id int) (string, model.Attributes, error)
	GetChannelMessageCount(conversation int, channel int) (int, error)
	GetChannelMessageByContentHash(conversation int, channel int, contenthash string) (int, error)
	GetChannelMessageBySignature(conversationID int, channelID int, signature string) (int, error)
	GetMostRecentMessages(conversation int, channel int, offset int, limit uint) ([]model.ConversationMessage, error)
	UpdateMessageAttribute(conversation int, channel int, id int, key string, value string) error
	SearchConversations(pattern string) string

	// EnhancedGetMessageById returns a json-encoded enhanced message, suitable for rendering in a UI
	EnhancedGetMessageById(conversation int, mid int) string

	// EnhancedGetMessageByContentHash returns a json-encoded enhanced message, suitable for rendering in a UI
	EnhancedGetMessageByContentHash(conversation int, hash string) string

	// EnhancedGetMessages returns a set of json-encoded enhanced messages, suitable for rendering in a UI
	EnhancedGetMessages(conversation int, index int, count uint) string

	// Server Token APIS
	// TODO move these to feature protected interfaces
	StoreCachedTokens(tokenServer string, tokens []*privacypass.Token)

	// Profile Management
	CheckPassword(password string) bool
	ChangePassword(oldpassword string, newpassword string, newpasswordAgain string) error
	ExportProfile(file string) error
	Delete()
	PublishEvent(resp event.Event)
	RegisterHook(hook ProfileHooks)
	UpdateExperiments(enabled bool, experiments map[string]bool)
	NotifySettingsUpdate(settings settings.GlobalSettings)
	IsFeatureEnabled(featureName string) bool
	SignMessage(blob []byte) ([]byte, error)

	// Used for Internal Bookkeeping by Extensions, **do not expose in autobindings**
	InternalInsertMessage(conversation int, channel int, author string, body string, attributes model.Attributes, signature []byte) (int, error)
}

// EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI.
type EnhancedMessage struct {
	model.Message
	ID           int // the actual ID of the message in the database (not the row number)
	LocalIndex   int // local index in the DB (row #). Can be empty (most calls supply it) but lookup by hash will fill it
	ContentHash  string
	ContactImage string
	Attributes   map[string]string
}
