package multidevice

import (
	"crypto/ecdsa"
	"database/sql"

	"github.com/google/uuid"

	"github.com/status-im/status-go/eth-node/crypto"
)

type InstallationMetadata struct {
	// The name of the device
	Name string `json:"name"`
	// The type of device
	DeviceType string `json:"deviceType"`
	// The FCMToken for mobile devices
	FCMToken string `json:"fcmToken"`
}

type Installation struct {
	// Identity is the string identity of the owner
	Identity string `json:"identity"`
	// The installation-id of the device
	ID string `json:"id"`
	// The last known protocol version of the device
	Version uint32 `json:"version"`
	// Enabled is whether the installation is enabled
	Enabled bool `json:"enabled"`
	// Timestamp is the last time we saw this device
	Timestamp int64 `json:"timestamp"`
	// InstallationMetadata
	InstallationMetadata *InstallationMetadata `json:"metadata"`
}

func (i *Installation) UniqueKey() string {
	return i.ID + i.Identity
}

type Config struct {
	MaxInstallations int
	ProtocolVersion  uint32
	InstallationID   string
}

type Multidevice struct {
	persistence *sqlitePersistence
	config      *Config
}

func New(db *sql.DB, config *Config) *Multidevice {
	return &Multidevice{
		config:      config,
		persistence: newSQLitePersistence(db),
	}
}

func (s *Multidevice) InstallationID() string {
	return s.config.InstallationID
}

func (s *Multidevice) GetActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
	identityC := crypto.CompressPubkey(identity)
	return s.persistence.GetActiveInstallations(s.config.MaxInstallations, identityC)
}

func (s *Multidevice) GetOurActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
	identityC := crypto.CompressPubkey(identity)
	installations, err := s.persistence.GetActiveInstallations(s.config.MaxInstallations-1, identityC)
	if err != nil {
		return nil, err
	}

	installations = append(installations, &Installation{
		ID:      s.config.InstallationID,
		Version: s.config.ProtocolVersion,
	})

	return installations, nil
}

func (s *Multidevice) GetOurInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
	var found bool
	identityC := crypto.CompressPubkey(identity)
	installations, err := s.persistence.GetInstallations(identityC)
	if err != nil {
		return nil, err
	}

	for _, installation := range installations {
		if installation.ID == s.config.InstallationID {
			found = true
			installation.Enabled = true
			installation.Version = s.config.ProtocolVersion
		}

	}
	if !found {
		installations = append(installations, &Installation{
			ID:      s.config.InstallationID,
			Enabled: true,
			Version: s.config.ProtocolVersion,
		})
	}

	return installations, nil
}

func (s *Multidevice) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) {
	return s.persistence.AddInstallations(identity, timestamp, installations, defaultEnabled)
}

func (s *Multidevice) SetInstallationMetadata(identity *ecdsa.PublicKey, installationID string, metadata *InstallationMetadata) error {
	identityC := crypto.CompressPubkey(identity)
	return s.persistence.SetInstallationMetadata(identityC, installationID, metadata)
}

func (s *Multidevice) SetInstallationName(identity *ecdsa.PublicKey, installationID string, name string) error {
	identityC := crypto.CompressPubkey(identity)
	return s.persistence.SetInstallationName(identityC, installationID, name)
}

func (s *Multidevice) EnableInstallation(identity *ecdsa.PublicKey, installationID string) error {
	identityC := crypto.CompressPubkey(identity)
	return s.persistence.EnableInstallation(identityC, installationID)
}

func (s *Multidevice) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
	myIdentityKeyC := crypto.CompressPubkey(myIdentityKey)
	return s.persistence.DisableInstallation(myIdentityKeyC, installationID)
}

func GenerateInstallationID() string {
	return uuid.New().String()
}