package protocol

import (
	"crypto/ecdsa"
	"errors"

	"github.com/status-im/status-go/eth-node/types"
	"github.com/status-im/status-go/protocol/common"
	"github.com/status-im/status-go/protocol/verification"
	"github.com/status-im/status-go/services/wallet/thirdparty"
)

// The activity center is a place where we store incoming notifications before
// they are shown to the users as new chats, in order to mitigate the impact of spam
// on the messenger

type ActivityCenterType int

const (
	ActivityCenterNotificationNoType ActivityCenterType = iota
	ActivityCenterNotificationTypeNewOneToOne
	ActivityCenterNotificationTypeNewPrivateGroupChat
	ActivityCenterNotificationTypeMention
	ActivityCenterNotificationTypeReply
	ActivityCenterNotificationTypeContactRequest
	ActivityCenterNotificationTypeCommunityInvitation
	ActivityCenterNotificationTypeCommunityRequest
	ActivityCenterNotificationTypeCommunityMembershipRequest
	ActivityCenterNotificationTypeCommunityKicked
	ActivityCenterNotificationTypeContactVerification
	ActivityCenterNotificationTypeContactRemoved
	ActivityCenterNotificationTypeNewKeypairAddedToPairedDevice
	ActivityCenterNotificationTypeOwnerTokenReceived
	ActivityCenterNotificationTypeOwnershipReceived
	ActivityCenterNotificationTypeOwnershipLost
	ActivityCenterNotificationTypeSetSignerFailed
	ActivityCenterNotificationTypeSetSignerDeclined
	ActivityCenterNotificationTypeShareAccounts
	ActivityCenterNotificationTypeCommunityTokenReceived
	ActivityCenterNotificationTypeFirstCommunityTokenReceived
	ActivityCenterNotificationTypeCommunityBanned
	ActivityCenterNotificationTypeCommunityUnbanned
)

type ActivityCenterMembershipStatus int

const (
	ActivityCenterMembershipStatusIdle ActivityCenterMembershipStatus = iota
	ActivityCenterMembershipStatusPending
	ActivityCenterMembershipStatusAccepted
	ActivityCenterMembershipStatusDeclined
	ActivityCenterMembershipStatusAcceptedPending
	ActivityCenterMembershipStatusDeclinedPending
	ActivityCenterMembershipOwnershipChanged
)

type ActivityCenterQueryParamsRead uint

const (
	ActivityCenterQueryParamsReadRead = iota + 1
	ActivityCenterQueryParamsReadUnread
	ActivityCenterQueryParamsReadAll
)

var ErrInvalidActivityCenterNotification = errors.New("invalid activity center notification")

type ActivityTokenData struct {
	ChainID       uint64                         `json:"chainId,omitempty"`
	CollectibleID thirdparty.CollectibleUniqueID `json:"collectibleId,omitempty"`
	TxHash        string                         `json:"txHash,omitempty"`
	WalletAddress string                         `json:"walletAddress,omitempty"`
	IsFirst       bool                           `json:"isFirst,omitempty"`
	// Community data
	CommunityID string `json:"communityId,omitempty"`
	// Token data
	Amount    string `json:"amount,omitempty"`
	Name      string `json:"name,omitempty"`
	Symbol    string `json:"symbol,omitempty"`
	ImageURL  string `json:"imageUrl,omitempty"`
	TokenType int    `json:"tokenType,omitempty"`
}

type ActivityCenterNotification struct {
	ID                        types.HexBytes                 `json:"id"`
	ChatID                    string                         `json:"chatId"`
	CommunityID               string                         `json:"communityId"`
	MembershipStatus          ActivityCenterMembershipStatus `json:"membershipStatus"`
	Name                      string                         `json:"name"`
	Author                    string                         `json:"author"`
	Type                      ActivityCenterType             `json:"type"`
	LastMessage               *common.Message                `json:"lastMessage"`
	Message                   *common.Message                `json:"message"`
	ReplyMessage              *common.Message                `json:"replyMessage"`
	Timestamp                 uint64                         `json:"timestamp"`
	Read                      bool                           `json:"read"`
	Dismissed                 bool                           `json:"dismissed"`
	Deleted                   bool                           `json:"deleted"`
	Accepted                  bool                           `json:"accepted"`
	ContactVerificationStatus verification.RequestStatus     `json:"contactVerificationStatus"`
	TokenData                 *ActivityTokenData             `json:"tokenData"`
	//Used for synchronization. Each update should increment the UpdatedAt.
	//The value should represent the time when the update occurred.
	UpdatedAt     uint64            `json:"updatedAt"`
	AlbumMessages []*common.Message `json:"albumMessages"`
}

func (n *ActivityCenterNotification) IncrementUpdatedAt(timesource common.TimeSource) {
	tNow := timesource.GetCurrentTime()
	// If updatead at is greater or equal than time now, we bump it
	if n.UpdatedAt >= tNow {
		n.UpdatedAt++
	} else {
		n.UpdatedAt = tNow
	}
}

type ActivityCenterNotificationsRequest struct {
	Cursor        string                        `json:"cursor"`
	Limit         uint64                        `json:"limit"`
	ActivityTypes []ActivityCenterType          `json:"activityTypes"`
	ReadType      ActivityCenterQueryParamsRead `json:"readType"`
}

type ActivityCenterCountRequest struct {
	ActivityTypes []ActivityCenterType          `json:"activityTypes"`
	ReadType      ActivityCenterQueryParamsRead `json:"readType"`
}

type ActivityCenterPaginationResponse struct {
	Cursor        string                        `json:"cursor"`
	Notifications []*ActivityCenterNotification `json:"notifications"`
}

type ActivityCenterCountResponse = map[ActivityCenterType]uint64

type ActivityCenterState struct {
	HasSeen   bool   `json:"hasSeen"`
	UpdatedAt uint64 `json:"updatedAt"`
}

func (n *ActivityCenterNotification) Valid() error {
	if len(n.ID) == 0 || n.Type == 0 || n.Timestamp == 0 {
		return ErrInvalidActivityCenterNotification
	}
	return nil
}

func showMentionOrReplyActivityCenterNotification(publicKey ecdsa.PublicKey, message *common.Message, chat *Chat, responseTo *common.Message) (bool, ActivityCenterType) {
	if chat == nil || !chat.Active || (!chat.CommunityChat() && !chat.PrivateGroupChat()) || chat.Muted {
		return false, ActivityCenterNotificationNoType
	}

	if message.Mentioned {
		return true, ActivityCenterNotificationTypeMention
	}

	publicKeyString := common.PubkeyToHex(&publicKey)
	if responseTo != nil && responseTo.From == publicKeyString {
		return true, ActivityCenterNotificationTypeReply
	}

	return false, ActivityCenterNotificationNoType
}