package domain

import (
	"strings"
	"time"
)

// Member is the person profile (stand-alone user_info)，uid 為數字主鍵。
type Member struct {
	UID               int64    `bson:"uid" json:"uid"`
	TenantID          string   `bson:"tenant_id" json:"tenant_id"`
	Email             string   `bson:"email" json:"email"`
	Phone             string   `bson:"phone,omitempty" json:"phone,omitempty"`
	DisplayName       string   `bson:"display_name" json:"display_name"`
	Nickname          string   `bson:"nickname,omitempty" json:"nickname,omitempty"`
	FullName          string   `bson:"full_name,omitempty" json:"full_name,omitempty"`
	Roles             []string `bson:"roles" json:"roles"`
	Status            string   `bson:"status" json:"status"`
	EmailVerified     bool     `bson:"email_verified" json:"email_verified"`
	EmailVerifiedAt   *int64   `bson:"email_verified_at,omitempty" json:"email_verified_at,omitempty"`
	PhoneVerified     bool     `bson:"phone_verified" json:"phone_verified"`
	PhoneVerifiedAt   *int64   `bson:"phone_verified_at,omitempty" json:"phone_verified_at,omitempty"`
	InviteCode        string   `bson:"invite_code,omitempty" json:"invite_code,omitempty"`
	ParentUID         *int64   `bson:"parent_uid,omitempty" json:"parent_uid,omitempty"`
	Bio               string   `bson:"bio,omitempty" json:"bio,omitempty"`
	Timezone          string   `bson:"timezone,omitempty" json:"timezone,omitempty"`
	AvatarURL         string   `bson:"avatar_url,omitempty" json:"avatar_url,omitempty"`
	NotifyEmail       bool     `bson:"notify_email,omitempty" json:"notify_email,omitempty"`
	GenderCode        int8     `bson:"gender_code,omitempty" json:"gender_code,omitempty"`
	Birthdate         int64    `bson:"birthdate,omitempty" json:"birthdate,omitempty"` // YYYYMMDD
	National          string   `bson:"national,omitempty" json:"national,omitempty"`
	Address           string   `bson:"address,omitempty" json:"address,omitempty"`
	PostCode          string   `bson:"post_code,omitempty" json:"post_code,omitempty"`
	PreferredLanguage string   `bson:"preferred_language,omitempty" json:"preferred_language,omitempty"`
	Currency          string   `bson:"currency,omitempty" json:"currency,omitempty"`
	// OnboardingStatus: pending | skipped | completed. Empty is treated as pending.
	OnboardingStatus  string   `bson:"onboarding_status,omitempty" json:"onboarding_status,omitempty"`
	OnboardingDoneAt  int64    `bson:"onboarding_done_at,omitempty" json:"onboarding_done_at,omitempty"`
	// Primary password for platform email login (also mirrored on Identity).
	// Must be JSON-tagged: monc Redis cache marshals with encoding/json;
	// json:"-" strips the hash and makes warm-cache login always fail.
	PasswordHash string `bson:"password_hash,omitempty" json:"password_hash,omitempty"`
	CreatedAt    int64  `bson:"created_at" json:"created_at"`
	UpdatedAt    int64  `bson:"updated_at" json:"updated_at"`
}

func (m Member) IsAdmin() bool {
	for _, r := range m.Roles {
		if r == RoleAdmin {
			return true
		}
	}
	return false
}

func (m Member) IsSuspended() bool {
	return m.Status == StatusSuspended
}

// Identity binds a login channel to a member (stand-alone account + account_uid).
type Identity struct {
	ID           string `bson:"_id,omitempty" json:"id,omitempty"`
	UID          int64  `bson:"uid" json:"uid"`
	LoginID      string `bson:"login_id" json:"login_id"` // email / phone / oauth subject
	Platform     string `bson:"platform" json:"platform"` // platform|google|line|apple
	AccountType  string `bson:"account_type" json:"account_type"`
	// json tag required for monc cache (see Member.PasswordHash).
	PasswordHash string `bson:"password_hash,omitempty" json:"password_hash,omitempty"`
	CreatedAt    int64  `bson:"created_at" json:"created_at"`
	UpdatedAt    int64  `bson:"updated_at" json:"updated_at"`
}

// UserSettings — AI/placement overrides
type UserSettings struct {
	UID                      int64  `bson:"uid" json:"uid"`
	Provider                 string `bson:"provider" json:"provider"`
	Model                    string `bson:"model" json:"model"`
	APIKeyConfigured         bool   `bson:"api_key_configured" json:"api_key_configured"`
	ResearchAPIKeyConfigured bool   `bson:"research_api_key_configured" json:"research_api_key_configured"`
	// APIKey — legacy single key (migrated into ProviderAPIKeys on save)
	APIKey string `bson:"api_key,omitempty" json:"-"`
	// ProviderAPIKeys — per-provider BYOK: "xai" | "opencode-go"
	ProviderAPIKeys     map[string]string `bson:"provider_api_keys,omitempty" json:"-"`
	ExaAPIKey           string            `bson:"exa_api_key,omitempty" json:"-"`
	WebSearchProvider   string            `bson:"web_search_provider" json:"web_search_provider"`
	ExpandStrategy      string            `bson:"expand_strategy" json:"expand_strategy"`
	ExaAPIKeyConfigured bool              `bson:"exa_api_key_configured" json:"exa_api_key_configured"`
	DevModeEnabled      bool              `bson:"dev_mode_enabled" json:"dev_mode_enabled"`
	UpdatedAt           int64             `bson:"updated_at" json:"updated_at"`
}

func DefaultSettings(uid int64) *UserSettings {
	return &UserSettings{
		UID: uid, Provider: "xai", Model: "grok-3",
		WebSearchProvider: "exa", ExpandStrategy: "hybrid",
		ProviderAPIKeys: map[string]string{},
	}
}

// KeyForProvider returns BYOK for provider (legacy APIKey fallback for current provider).
func (s *UserSettings) KeyForProvider(provider string) string {
	if s == nil {
		return ""
	}
	provider = strings.ToLower(strings.TrimSpace(provider))
	if provider == "" {
		provider = s.Provider
	}
	if s.ProviderAPIKeys != nil {
		if k := strings.TrimSpace(s.ProviderAPIKeys[provider]); k != "" {
			return k
		}
	}
	// legacy single key only applies to currently selected provider
	if provider == s.Provider || s.Provider == "" {
		return strings.TrimSpace(s.APIKey)
	}
	return ""
}

// SetKeyForProvider stores BYOK under provider map.
func (s *UserSettings) SetKeyForProvider(provider, key string) {
	if s == nil {
		return
	}
	provider = strings.ToLower(strings.TrimSpace(provider))
	if s.ProviderAPIKeys == nil {
		s.ProviderAPIKeys = map[string]string{}
	}
	key = strings.TrimSpace(key)
	if key == "" {
		delete(s.ProviderAPIKeys, provider)
	} else {
		s.ProviderAPIKeys[provider] = key
	}
	// keep legacy field in sync for current provider
	if provider == s.Provider {
		s.APIKey = key
		s.APIKeyConfigured = key != ""
		s.ResearchAPIKeyConfigured = key != ""
	}
}

// HasKeyForProvider — UI configured flag.
func (s *UserSettings) HasKeyForProvider(provider string) bool {
	return strings.TrimSpace(s.KeyForProvider(provider)) != ""
}

func NowNano() int64 { return time.Now().UTC().UnixNano() }
