package entities

import (
	"time"

	"github.com/google/uuid"
	"github.com/rwadurian/mpc-system/services/account/domain/value_objects"
)

// Account represents a user account with MPC-based authentication
type Account struct {
	ID              value_objects.AccountID
	Username        string  // Required: auto-generated by identity-service
	Email           *string // Optional: for anonymous accounts
	Phone           *string
	PublicKey       []byte // MPC group public key
	KeygenSessionID uuid.UUID
	ThresholdN      int
	ThresholdT      int
	Status          value_objects.AccountStatus
	CreatedAt       time.Time
	UpdatedAt       time.Time
	LastLoginAt     *time.Time
	// SigningParties is a list of party IDs designated for signing operations.
	// If nil or empty, all active parties will be used for signing.
	// Must contain exactly ThresholdT parties when set.
	SigningParties []string
}

// NewAccount creates a new Account
func NewAccount(
	username string,
	email string,
	publicKey []byte,
	keygenSessionID uuid.UUID,
	thresholdN int,
	thresholdT int,
) *Account {
	now := time.Now().UTC()
	var emailPtr *string

	if email != "" {
		emailPtr = &email
	}

	return &Account{
		ID:              value_objects.NewAccountID(),
		Username:        username,
		Email:           emailPtr,
		PublicKey:       publicKey,
		KeygenSessionID: keygenSessionID,
		ThresholdN:      thresholdN,
		ThresholdT:      thresholdT,
		Status:          value_objects.AccountStatusActive,
		CreatedAt:       now,
		UpdatedAt:       now,
	}
}

// SetPhone sets the phone number
func (a *Account) SetPhone(phone string) {
	a.Phone = &phone
	a.UpdatedAt = time.Now().UTC()
}

// UpdateLastLogin updates the last login timestamp
func (a *Account) UpdateLastLogin() {
	now := time.Now().UTC()
	a.LastLoginAt = &now
	a.UpdatedAt = now
}

// Suspend suspends the account
func (a *Account) Suspend() error {
	if a.Status == value_objects.AccountStatusRecovering {
		return ErrAccountInRecovery
	}
	a.Status = value_objects.AccountStatusSuspended
	a.UpdatedAt = time.Now().UTC()
	return nil
}

// Lock locks the account
func (a *Account) Lock() error {
	if a.Status == value_objects.AccountStatusRecovering {
		return ErrAccountInRecovery
	}
	a.Status = value_objects.AccountStatusLocked
	a.UpdatedAt = time.Now().UTC()
	return nil
}

// Activate activates the account
func (a *Account) Activate() {
	a.Status = value_objects.AccountStatusActive
	a.UpdatedAt = time.Now().UTC()
}

// StartRecovery marks the account as recovering
func (a *Account) StartRecovery() error {
	if !a.Status.CanInitiateRecovery() {
		return ErrCannotInitiateRecovery
	}
	a.Status = value_objects.AccountStatusRecovering
	a.UpdatedAt = time.Now().UTC()
	return nil
}

// CompleteRecovery completes the recovery process with new public key
func (a *Account) CompleteRecovery(newPublicKey []byte, newKeygenSessionID uuid.UUID) {
	a.PublicKey = newPublicKey
	a.KeygenSessionID = newKeygenSessionID
	a.Status = value_objects.AccountStatusActive
	a.UpdatedAt = time.Now().UTC()
}

// CanLogin checks if the account can login
func (a *Account) CanLogin() bool {
	return a.Status.CanLogin()
}

// IsActive checks if the account is active
func (a *Account) IsActive() bool {
	return a.Status == value_objects.AccountStatusActive
}

// Validate validates the account data
func (a *Account) Validate() error {
	if a.Username == "" {
		return ErrInvalidUsername
	}
	// Email is optional, but if provided must be valid (checked by binding)
	if len(a.PublicKey) == 0 {
		return ErrInvalidPublicKey
	}
	if a.ThresholdT > a.ThresholdN || a.ThresholdT <= 0 {
		return ErrInvalidThreshold
	}
	return nil
}

// SetSigningParties sets the designated signing parties for this account
// partyIDs must contain exactly ThresholdT + 1 parties (required signers for TSS)
// For 2-of-3: ThresholdT=1, so T+1=2 signers required
func (a *Account) SetSigningParties(partyIDs []string) error {
	requiredSigners := a.ThresholdT + 1
	if len(partyIDs) != requiredSigners {
		return ErrInvalidSigningPartiesCount
	}
	// Check for duplicates
	seen := make(map[string]bool)
	for _, id := range partyIDs {
		if id == "" {
			return ErrInvalidPartyID
		}
		if seen[id] {
			return ErrDuplicatePartyID
		}
		seen[id] = true
	}
	a.SigningParties = partyIDs
	a.UpdatedAt = time.Now().UTC()
	return nil
}

// ClearSigningParties removes the signing parties configuration
// After clearing, all active parties will be used for signing
func (a *Account) ClearSigningParties() {
	a.SigningParties = nil
	a.UpdatedAt = time.Now().UTC()
}

// HasSigningPartiesConfig returns true if signing parties are configured
func (a *Account) HasSigningPartiesConfig() bool {
	return len(a.SigningParties) > 0
}

// GetSigningParties returns the configured signing parties
// Returns nil if not configured (meaning all active parties should be used)
func (a *Account) GetSigningParties() []string {
	if len(a.SigningParties) == 0 {
		return nil
	}
	// Return a copy to prevent modification
	result := make([]string, len(a.SigningParties))
	copy(result, a.SigningParties)
	return result
}

// Account errors
var (
	ErrInvalidUsername             = &AccountError{Code: "INVALID_USERNAME", Message: "username is required"}
	ErrInvalidEmail                = &AccountError{Code: "INVALID_EMAIL", Message: "email is required"}
	ErrInvalidPublicKey            = &AccountError{Code: "INVALID_PUBLIC_KEY", Message: "public key is required"}
	ErrInvalidThreshold            = &AccountError{Code: "INVALID_THRESHOLD", Message: "invalid threshold configuration"}
	ErrAccountInRecovery           = &AccountError{Code: "ACCOUNT_IN_RECOVERY", Message: "account is in recovery mode"}
	ErrCannotInitiateRecovery      = &AccountError{Code: "CANNOT_INITIATE_RECOVERY", Message: "cannot initiate recovery in current state"}
	ErrAccountNotActive            = &AccountError{Code: "ACCOUNT_NOT_ACTIVE", Message: "account is not active"}
	ErrAccountNotFound             = &AccountError{Code: "ACCOUNT_NOT_FOUND", Message: "account not found"}
	ErrDuplicateUsername           = &AccountError{Code: "DUPLICATE_USERNAME", Message: "username already exists"}
	ErrDuplicateEmail              = &AccountError{Code: "DUPLICATE_EMAIL", Message: "email already exists"}
	ErrInvalidSigningPartiesCount  = &AccountError{Code: "INVALID_SIGNING_PARTIES_COUNT", Message: "signing parties count must equal threshold T + 1 (required signers)"}
	ErrInvalidPartyID              = &AccountError{Code: "INVALID_PARTY_ID", Message: "party ID cannot be empty"}
	ErrDuplicatePartyID            = &AccountError{Code: "DUPLICATE_PARTY_ID", Message: "duplicate party ID in signing parties"}
)

// AccountError represents an account domain error
type AccountError struct {
	Code    string
	Message string
}

func (e *AccountError) Error() string {
	return e.Message
}
