package firebase

import (
	"context"
	"fmt"
	"log"
	"os"
	"slices"
	"sync"
	"sync/atomic"
	"time"

	"cloud.google.com/go/firestore"
	firebase "firebase.google.com/go"
	"firebase.google.com/go/auth"
	"github.com/gin-gonic/gin"
	"golang.org/x/time/rate"
	"google.golang.org/api/option"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/keepalive"
	"google.golang.org/grpc/status"
)

// Firebase app
var firebaseAppReimage *firebase.App = nil

// the firebase auth client
var authClientReimage *auth.Client

// the firestore client
var firestoreClientReimage *firestore.Client
var firestoreClientConsole *firestore.Client

// cache timeout
var cacheTimeout int64 = 10 * 60 * 1000

// firestore email (regular user) data
type FirestoreEmailProperties struct {
	lastUpdateTime       atomic.Int64   `firestore:"omitempty"`
	email                string         `firestore:"omitempty"`
	documentID           string         `firestore:"omitempty"`
	TokensRemodel        *int           `firestore:"tokens_remodel,omitempty" json:"tokens_remodel"`
	TokensPaint          *int           `firestore:"tokens_paint,omitempty" json:"tokens_paint"`
	TokensRoof           *int           `firestore:"tokens_roof,omitempty" json:"tokens_roof"`
	TokensStaging        *int           `firestore:"tokens_staging,omitempty" json:"tokens_staging"`
	TokensLandscaping    *int           `firestore:"tokens_landscaping,omitempty" json:"tokens_landscaping"`
	TokensRoomsy         *int           `firestore:"tokens_roomsy,omitempty" json:"tokens_roomsy"`
	TokensStarting       int            `firestore:"tokens_starting,omitempty" json:"tokens_starting"`
	XP                   map[string]int `firestore:"xp,omitempty" json:"xp"`
	Banned               string         `firestore:"banned,omitempty" json:"banned"`
	BannedTemp           bool           `firestore:"omitempty" json:"banned_temp"`
	Refill               int            `firestore:"refill,omitempty" json:"refill,omitempty"`
	FCMTokensRemodel     []string       `firestore:"fcm_tokens_remodel,omitempty" json:"fcm_tokens_remodel"`
	FCMTokensPaint       []string       `firestore:"fcm_tokens_paint,omitempty" json:"fcm_tokens_paint"`
	FCMTokensRoof        []string       `firestore:"fcm_tokens_roof,omitempty" json:"fcm_tokens_roof"`
	FCMTokensStaging     []string       `firestore:"fcm_tokens_staging,omitempty" json:"fcm_tokens_staging"`
	FCMTokensLandscaping []string       `firestore:"fcm_tokens_landscaping,omitempty" json:"fcm_tokens_landscaping"`
	FCMTokensRoomsy      []string       `firestore:"fcm_tokens_roomsy,omitempty" json:"fcm_tokens_roomsy"`
	limiter              *rate.Limiter  `firestore:"omitempty"`
}

// firestore account (API account) data
type FirestoreAccountProperties struct {
	lastUpdateTime int64         `firestore:"omitempty"`
	documentID     string        `firestore:"omitempty"`
	Email          string        `firestore:"email,omitempty" json:"email"`
	CompanyName    string        `firestore:"company_name,omitempty" json:"company_name"`
	TokenCredits   float64       `firestore:"token_credits,omitempty" json:"token_credits"`
	Keys           []string      `firestore:"keys,omitempty" json:"keys"`
	TokenRateLimit rate.Limit    `firestore:"token_rate_limit" json:"token_rate_limit"`
	Invoicing      bool          `firestore:"invoicing,omitempty" json:"invoicing"`
	Banned         string        `firestore:"banned,omitempty" json:"banned"`
	limiter        *rate.Limiter `firestore:"omitempty"`
}

// firestore email properties cache - since we only read from one location
// we can cache properties here because there is no other source of change
// on write we update this and the database
// we use emails as the primary key instead of uids, because the user can easily delete
// their account and create a new one to get new tokens
var firebaseEmailPropertiesCache map[string]*FirestoreEmailProperties = make(map[string]*FirestoreEmailProperties)
var firebaseEmailPropertiesCacheLock sync.Mutex

// firestore account properties cache - since we only read from one location
// we can cache properties here because there is no other source of change
// this is a map from api keys to the associate acount properties since we lookup by api key
var firebaseAccountPropertiesCache map[string]*FirestoreAccountProperties = make(map[string]*FirestoreAccountProperties)
var firebaseAccountPropertiesCacheLock sync.Mutex

func initialize(reimageKeyPath string, consoleKeyPath string) {
	var err error
	opt := option.WithCredentialsFile(reimageKeyPath)
	opt2 := option.WithGRPCDialOption(grpc.WithKeepaliveParams(keepalive.ClientParameters{
		Time: 2 * time.Minute,
	}))
	configReimage := &firebase.Config{ProjectID: "reimage-ai"}

	log.Print("Initializing Firebase...")
	firebaseAppReimage, err = firebase.NewApp(context.Background(), configReimage, opt, opt2)
	if err != nil {
		log.Fatalf("error initializing app: %v\n", err)
	}

	log.Print("Initializing Firebase Auth...")
	authClientReimage, err = firebaseAppReimage.Auth(context.Background())
	if err != nil {
		log.Fatalf("error initializing auth: %v\n", err)
	}

	log.Print("Initializing Firestore...")
	firestoreClientReimage, err = firestore.NewClient(context.Background(), "reimage-ai", option.WithCredentialsFile(reimageKeyPath))
	if err != nil {
		log.Fatalf("error initializing auth: %v\n", err)
	}
	firestoreClientConsole, err = firestore.NewClient(context.Background(), "reimage-console", option.WithCredentialsFile(consoleKeyPath))
	if err != nil {
		log.Fatalf("error initializing auth: %v\n", err)
	}
}

// Init initializes all firebase assets
func Init() {
	var err error
	dirname, err := os.UserHomeDir()
	if err != nil {
		log.Fatal(err)
	}

	var paths []string = []string{"/etc/broker/", "./", "../../", fmt.Sprintf("%s/certs/firebase/", dirname)}
	for _, s := range paths {
		reimageFullpath := fmt.Sprintf("%sbroker-service-key.json", s)
		consoleFullpath := fmt.Sprintf("%sbroker-service-key-console.json", s)
		if _, err := os.Stat(reimageFullpath); err == nil {
			fullpath := fmt.Sprintf("%sbroker-service-key.json", s)
			log.Printf("Loading key from: %s %s\n", fullpath, consoleFullpath)
			initialize(reimageFullpath, consoleFullpath)
			break
		}
	}

	if firebaseAppReimage == nil {
		log.Fatalf("Error: Failed to initialize firebase\n")
	}

	// geoDB, err = geoip2.Open("/var/lib/broker/GeoLite2-Country.mmdb")
	// if err != nil {
	// 	log.Fatal(err)
	// }

	// GetEmailProperties("dirk.morris@gmail.com") // dirk.morris@gmail.com
	// GetEmailProperties("dmorris@metaloft.com") // dmorris@metaloft.com

	// GetEmailProperties("dirk.morris@gmail.com") // dirk.morris@gmail.com
	// GetEmailProperties("dmorris@metaloft.com") // dmorris@metaloft.com
}

// UID returns uid associated with that token
func UID(idToken string) (error, string) {
	if idToken == "" {
		return fmt.Errorf("empty token"), ""
	}

	err, token := getIDToken(idToken)
	if err != nil {
		return err, ""
	}
	if token == nil {
		return fmt.Errorf("empty token"), ""
	}

	return nil, token.UID
}

// Email returns email associated with that token
func Email(idToken string) (error, string) {
	if idToken == "" {
		return fmt.Errorf("empty token"), ""
	}

	err, token := getIDToken(idToken)
	if err != nil {
		return err, ""
	}
	if token == nil {
		return fmt.Errorf("empty token"), ""
	}
	m := token.Firebase.Identities

	val, ok := m["email"]
	if !ok {
		return fmt.Errorf("no email associated with token: %v", token.UID), ""
	}

	if x, ok := val.([]any); ok {
		if len(x) < 1 {
			return fmt.Errorf("invalid email: %v", val), ""
		}
		val = x[0]
	}

	return nil, fmt.Sprintf("%v", val)
}

// Print debugging information
func AuthDebugPrint(c *gin.Context) {
	log.Printf("AUTH token: %v\n", c.GetHeader("authorization"))
	token, err := authClientReimage.VerifyIDToken(context.Background(), c.GetHeader("authorization"))
	if err != nil {
		log.Printf("Error getting auth: %v\n", err)
		return
	}
	log.Printf("UID: %v\n", token.UID)
	log.Printf("AuthTime: %v\n", token.AuthTime)
	log.Printf("Issuer: %v\n", token.Issuer)
	log.Printf("Audience: %v\n", token.Audience)
	log.Printf("Subject: %v\n", token.Subject)
	log.Printf("Firebase: %v\n", token.Firebase)
	log.Printf("Tenant: %v\n", token.Firebase.Tenant)
	log.Printf("Identities: %v\n", token.Firebase.Identities)
	log.Printf("Claims: %v\n", token.Claims)
}

// Clears the email cache for a specific uid
func ClearEmailCache(email string) {
	firebaseEmailPropertiesCacheLock.Lock()
	delete(firebaseEmailPropertiesCache, email)
	firebaseEmailPropertiesCacheLock.Unlock()
}

// get email properties (or create if necessary)
func GetEmailProperties(email string) *FirestoreEmailProperties {
	var err error
	var data *firestore.DocumentSnapshot

	// check cache
	now := time.Now().UnixMilli()
	firebaseEmailPropertiesCacheLock.Lock()
	cacheValue, ok := firebaseEmailPropertiesCache[email]
	firebaseEmailPropertiesCacheLock.Unlock()
	if ok && (now-cacheValue.lastUpdateTime.Load()) < cacheTimeout {
		return cacheValue
	}

	ctx := context.Background()

	data, err = firestoreClientReimage.Doc("emails/" + email).Get(ctx)
	if err != nil && status.Code(err) == codes.NotFound {
		var startingTokens int = getStartingTokenCount()
		_, err = firestoreClientReimage.Doc("emails/"+email).Create(ctx, map[string]any{
			"tokens_remodel":      startingTokens,
			"tokens_paint":        startingTokens,
			"tokens_roof":         startingTokens,
			"tokens_staging":      startingTokens,
			"tokens_landscaping":  startingTokens,
			"tokens_roomsy":       startingTokens,
			"tokens_starting":     startingTokens,
			"last_tokens_awarded": time.Now(),
			"creation_date":       time.Now(),
			"refill":              0,
		})
		if err != nil && status.Code(err) == codes.AlreadyExists {
			//This may happen if we do this multiple times asynchronously
			//Or sometimes I believe the client returns it AlreadyExists ayway
			//log.Printf("Failed to create user: %v %v\n", email, err)
		} else if err != nil {
			log.Printf("Error: Failed to create user: %v %v\n", email, err)
		} else {
			log.Printf("Created new user: %v (tokens: %v)\n", email, startingTokens)
		}
		data, err = firestoreClientReimage.Doc("emails/" + email).Get(ctx)
	}
	if err != nil {
		log.Printf("Error: Failed to fetch user: %v %v\n", email, err)
		return nil
	}
	// json, err := json.MarshalIndent(data.Data(), "", "  ")
	// fmt.Println(string(json))

	var emailProperties FirestoreEmailProperties
	emailProperties.email = email
	emailProperties.documentID = data.Ref.ID
	emailProperties.lastUpdateTime.Store(now)
	if err := data.DataTo(&emailProperties); err != nil {
		log.Printf("Error: Failed to serialize emailProperties: %v\n", err)
		return nil
	}

	// initialize token limiter
	// 10 tokens per minute max - hardcoded
	// multiple by 100 because limiter doesnt support floats 1 token = 100 rate
	if cacheValue != nil {
		emailProperties.limiter = cacheValue.limiter
	} else {
		emailProperties.limiter = rate.NewLimiter((5*100.0)/60, 10*100)
	}

	// initialize decent defaults (fixes old accounts)
	if emailProperties.TokensStarting == 0 {
		emailProperties.TokensStarting = getStartingTokenCount()
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_starting", FieldPath: nil, Value: emailProperties.TokensStarting}})
	}
	if emailProperties.TokensRemodel == nil {
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_remodel", FieldPath: nil, Value: emailProperties.TokensStarting}})
		emailProperties.TokensRemodel = &emailProperties.TokensStarting
	}
	if emailProperties.TokensPaint == nil {
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_paint", FieldPath: nil, Value: emailProperties.TokensStarting}})
		emailProperties.TokensPaint = &emailProperties.TokensStarting
	}
	if emailProperties.TokensRoof == nil {
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_roof", FieldPath: nil, Value: emailProperties.TokensStarting}})
		emailProperties.TokensRoof = &emailProperties.TokensStarting
	}
	if emailProperties.TokensStaging == nil {
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_staging", FieldPath: nil, Value: emailProperties.TokensStarting}})
		emailProperties.TokensStaging = &emailProperties.TokensStarting
	}
	if emailProperties.TokensLandscaping == nil {
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_landscaping", FieldPath: nil, Value: emailProperties.TokensStarting}})
		emailProperties.TokensLandscaping = &emailProperties.TokensStarting
	}
	if emailProperties.TokensRoomsy == nil {
		_, err = firestoreClientReimage.Doc("emails/"+email).Update(context.Background(), []firestore.Update{{Path: "tokens_roomsy", FieldPath: nil, Value: emailProperties.TokensStarting}})
		emailProperties.TokensRoomsy = &emailProperties.TokensStarting
	}

	firebaseEmailPropertiesCacheLock.Lock()
	// recheck cache value in case another thread has set it in meantime
	cacheValue, ok = firebaseEmailPropertiesCache[email]
	if ok && (now-cacheValue.lastUpdateTime.Load()) < cacheTimeout {
		firebaseEmailPropertiesCacheLock.Unlock()
		return cacheValue
	}
	firebaseEmailPropertiesCache[email] = &emailProperties
	firebaseEmailPropertiesCacheLock.Unlock()

	return &emailProperties
}

// get account properties
func GetAccountPropertiesByKey(key string) *FirestoreAccountProperties {
	var err error
	var data *firestore.DocumentSnapshot

	// check cache
	now := time.Now().UnixMilli()
	firebaseAccountPropertiesCacheLock.Lock()
	cacheValue, ok := firebaseAccountPropertiesCache[key]
	firebaseAccountPropertiesCacheLock.Unlock()
	if ok && (now-cacheValue.lastUpdateTime) < cacheTimeout {
		return cacheValue
	}

	ctx := context.Background()

	query := firestoreClientConsole.Collection("accounts").Where("keys", "array-contains", key).Documents(ctx)
	data, err = query.Next()
	if err != nil {
		log.Printf("Error: Failed to fetch account: %v %v\n", key, err)
		return nil
	}

	var accountProperties FirestoreAccountProperties
	accountProperties.documentID = data.Ref.ID
	accountProperties.lastUpdateTime = now
	if err := data.DataTo(&accountProperties); err != nil {
		log.Printf("Error: Failed to serialize emailProperties: %v\n", err)
		return nil
	}

	// initialize token limiter
	// multiple by 100 because limiter doesnt support floats 1 token = 100 rate
	if cacheValue != nil {
		accountProperties.limiter = cacheValue.limiter
	} else {
		accountProperties.limiter = rate.NewLimiter((accountProperties.TokenRateLimit*100)/60, 10*100) // token_rate_limit tokes per minute max
	}

	// add to cache
	firebaseAccountPropertiesCacheLock.Lock()
	// recheck cache value in case another thread has set it in meantime
	cacheValue, ok = firebaseAccountPropertiesCache[key]
	if ok && (now-cacheValue.lastUpdateTime) < cacheTimeout {
		firebaseAccountPropertiesCacheLock.Unlock()
		return cacheValue
	}
	for i := 0; i < len(accountProperties.Keys); i++ {
		firebaseAccountPropertiesCache[accountProperties.Keys[i]] = &accountProperties
	}
	firebaseAccountPropertiesCacheLock.Unlock()

	return &accountProperties
}

// increment token use
func (p *FirestoreAccountProperties) IncrementTokenUsage(value float64) {
	var err error

	// if there are enough credits to cover this transaction - use the credits
	if p.TokenCredits >= value {
		p.TokenCredits = p.TokenCredits - value
		_, err = firestoreClientConsole.Doc("accounts/"+p.documentID).Update(context.Background(), []firestore.Update{{Path: "token_credits", Value: firestore.Increment(-value)}})
		if err != nil {
			log.Printf("Token credits update error: %s\n", err)
		}
		return
	}

	// if you don't have enough credits - add to invoice
	if !p.Invoicing {
		log.Printf("WARNING: invoicing not set on account but no credits: %v\n", p.Email)
	}
	var fieldName = fmt.Sprintf("token_usage.%s", time.Now().Format("2006_01_02"))
	_, err = firestoreClientConsole.Doc("accounts/"+p.documentID).Update(context.Background(), []firestore.Update{{Path: fieldName, Value: firestore.Increment(value)}})
	if err != nil {
		log.Printf("Token update error: %s\n", err)
	}
}

func (p *FirestoreEmailProperties) IncrementXP(app string, value int) {
	var err error

	validApps := []string{"remodel", "paint", "roof", "staging", "landscaping", "roomsy"}
	if !slices.Contains(validApps, app) {
		log.Printf("Error: Invalid app: %s\n", app)
		return
	}
	if value < 1 {
		return
	}

	if p.XP == nil {
		p.XP = make(map[string]int, 10)
	}
	if _, ok := p.XP[app]; !ok {
		p.XP[app] = 0
	}
	newValue := p.XP[app] + value

	// update local value in cache
	p.XP[app] = newValue

	// update remote value in firebase
	var fieldName = fmt.Sprintf("xp.%s", app)
	_, err = firestoreClientReimage.Doc("emails/"+p.documentID).Update(context.Background(), []firestore.Update{{Path: fieldName, Value: firestore.Increment(value)}})
	if err != nil {
		log.Printf("Error: XP update: %s\n", err)
	}
}

// reserve a number of tokens from the rate limit
func (p *FirestoreAccountProperties) CheckRateLimit(tokenCount float64) bool {
	if p == nil {
		return false
	}
	// multiple by 100 because limiter doesnt support floats 1 token = 100 rate
	ok := p.limiter.AllowN(time.Now(), int(tokenCount*100))
	//log.Printf("Rate Check: %v Allow %v = %v (avail %v)\n", p.accountName, tokenCount*100, ok, p.limiter.Tokens())
	return ok
}

// get the banned policy from the account (true if banned)
func (p *FirestoreEmailProperties) GetBannedTemp() bool {
	if p == nil {
		return false
	} else {
		return p.BannedTemp
	}
}

// apply a temporary ban to this user
func (p *FirestoreEmailProperties) ApplyTempBan() {
	log.Printf("Alert: %v temp banned!", p.email)
	p.BannedTemp = true
	p.lastUpdateTime.Store(time.Now().UnixMilli())
}

// get the number of available tokens from the account
func (p *FirestoreEmailProperties) GetTokenCount(name string) int {
	switch name {
	case "remodel":
		return *p.TokensRemodel
	case "paint":
		return *p.TokensPaint
	case "roof":
		return *p.TokensRoof
	case "staging":
		return *p.TokensStaging
	case "landscaping":
		return *p.TokensLandscaping
	case "roomsy":
		return *p.TokensRoomsy
	default:
		log.Printf("Error: Unknown token name: %v\n", name)
		// default to remodel
		return *p.TokensRemodel
	}
}

// decrement a token from the email account
func (p *FirestoreEmailProperties) DecrementTokenCount(tokenName string) {
	var err error
	switch tokenName {
	case "paint":
		*p.TokensPaint = *p.TokensPaint - 1
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "tokens_paint", FieldPath: nil, Value: firestore.Increment(-1)}})
	case "roof":
		*p.TokensRoof = *p.TokensRoof - 1
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "tokens_roof", FieldPath: nil, Value: firestore.Increment(-1)}})
	case "staging":
		*p.TokensStaging = *p.TokensStaging - 1
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "tokens_staging", FieldPath: nil, Value: firestore.Increment(-1)}})
	case "landscaping":
		*p.TokensLandscaping = *p.TokensLandscaping - 1
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "tokens_landscaping", FieldPath: nil, Value: firestore.Increment(-1)}})
	case "roomsy":
		*p.TokensRoomsy = *p.TokensRoomsy - 1
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "tokens_roomsy", FieldPath: nil, Value: firestore.Increment(-1)}})
	default:
		*p.TokensRemodel = *p.TokensRemodel - 1
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "tokens_remodel", FieldPath: nil, Value: firestore.Increment(-1)}})
	}
	if err != nil {
		log.Printf("Token update error: %s\n", err)
	}
}

// reserver a number of tokens from the rate limit
func (p *FirestoreEmailProperties) CheckRateLimit(tokenCount float64) bool {
	if p == nil {
		return false
	}
	// multiple by 100 because limiter doesnt support floats 1 token = 100 rate
	ok := p.limiter.AllowN(time.Now(), int(tokenCount*100))
	//log.Printf("Rate Check: %v Allow %v = %v (avail %v)\n", p.email, tokenCount*100, ok, p.limiter.Tokens())
	return ok
}

// updateFcmTokens updates the FCM tokens for the user identified by the given email.
func (p *FirestoreEmailProperties) UpdateFcmTokens(newToken string, tokenName string) error {
	if p.FCMTokensRemodel == nil {
		p.FCMTokensRemodel = make([]string, 0)
	}
	if p.FCMTokensPaint == nil {
		p.FCMTokensPaint = make([]string, 0)
	}
	if p.FCMTokensRoof == nil {
		p.FCMTokensRoof = make([]string, 0)
	}
	if p.FCMTokensStaging == nil {
		p.FCMTokensStaging = make([]string, 0)
	}
	if p.FCMTokensLandscaping == nil {
		p.FCMTokensLandscaping = make([]string, 0)
	}
	if p.FCMTokensRoomsy == nil {
		p.FCMTokensRoomsy = make([]string, 0)
	}

	var err error
	switch tokenName {
	case "remodel":
		if slices.Contains(p.FCMTokensRemodel, newToken) {
			return nil
		}
		firebaseEmailPropertiesCacheLock.Lock()
		p.FCMTokensRemodel = append(p.FCMTokensRemodel, newToken)
		if len(p.FCMTokensRemodel) > 3 {
			p.FCMTokensRemodel = p.FCMTokensRemodel[1:]
		}
		firebaseEmailPropertiesCacheLock.Unlock()
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "fcm_tokens_remodel", FieldPath: nil, Value: p.FCMTokensRemodel}})
	case "paint":
		if slices.Contains(p.FCMTokensPaint, newToken) {
			return nil
		}
		firebaseEmailPropertiesCacheLock.Lock()
		p.FCMTokensPaint = append(p.FCMTokensPaint, newToken)
		if len(p.FCMTokensPaint) > 3 {
			p.FCMTokensPaint = p.FCMTokensPaint[1:]
		}
		firebaseEmailPropertiesCacheLock.Unlock()
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "fcm_tokens_paint", FieldPath: nil, Value: p.FCMTokensPaint}})
	case "roof":
		if slices.Contains(p.FCMTokensRoof, newToken) {
			return nil
		}
		firebaseEmailPropertiesCacheLock.Lock()
		p.FCMTokensRoof = append(p.FCMTokensRoof, newToken)
		if len(p.FCMTokensRoof) > 3 {
			p.FCMTokensRoof = p.FCMTokensRoof[1:]
		}
		firebaseEmailPropertiesCacheLock.Unlock()
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "fcm_tokens_roof", FieldPath: nil, Value: p.FCMTokensRoof}})
	case "staging":
		if slices.Contains(p.FCMTokensStaging, newToken) {
			return nil
		}
		firebaseEmailPropertiesCacheLock.Lock()
		p.FCMTokensStaging = append(p.FCMTokensStaging, newToken)
		if len(p.FCMTokensStaging) > 3 {
			p.FCMTokensStaging = p.FCMTokensStaging[1:]
		}
		firebaseEmailPropertiesCacheLock.Unlock()
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "fcm_tokens_staging", FieldPath: nil, Value: p.FCMTokensStaging}})
	case "landscaping":
		if slices.Contains(p.FCMTokensLandscaping, newToken) {
			return nil
		}
		firebaseEmailPropertiesCacheLock.Lock()
		p.FCMTokensLandscaping = append(p.FCMTokensLandscaping, newToken)
		if len(p.FCMTokensLandscaping) > 3 {
			p.FCMTokensLandscaping = p.FCMTokensLandscaping[1:]
		}
		firebaseEmailPropertiesCacheLock.Unlock()
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "fcm_tokens_landscaping", FieldPath: nil, Value: p.FCMTokensLandscaping}})
	case "roomsy":
		if slices.Contains(p.FCMTokensRoomsy, newToken) {
			return nil
		}
		firebaseEmailPropertiesCacheLock.Lock()
		p.FCMTokensRoomsy = append(p.FCMTokensRoomsy, newToken)
		if len(p.FCMTokensRoomsy) > 3 {
			p.FCMTokensRoomsy = p.FCMTokensRoomsy[1:]
		}
		firebaseEmailPropertiesCacheLock.Unlock()
		_, err = firestoreClientReimage.Doc("emails/"+p.email).Update(context.Background(), []firestore.Update{{Path: "fcm_tokens_roomsy", FieldPath: nil, Value: p.FCMTokensRoomsy}})
	}

	// Update the FCM tokens in the database
	if err != nil {
		log.Printf("FCM Token update error: %s\n", err)
	}

	if err != nil {
		log.Printf("Failed to update FCM tokens for user %s: %v", p.email, err)
		return err
	}

	//log.Printf("Successfully updated FCM tokens %s for user %s",p.FCMTokens, email)
	return nil
}

// FirestoreClientReimage returns the Firestore client for the reimage-ai project.
func FirestoreClientReimage() *firestore.Client {
	return firestoreClientReimage
}

// VerifyIDToken verifies a Firebase ID token and returns an error if invalid.
func VerifyIDToken(idToken string) (error, *auth.Token) {
	return getIDToken(idToken)
}

// getIDToken verifies a token
func getIDToken(idToken string) (error, *auth.Token) {
	token, err := authClientReimage.VerifyIDToken(context.Background(), idToken)
	if err != nil {
		log.Printf("Error: Failed to verify ID token: %v\n", err)
		return err, nil
	}

	return nil, token
}

// determines the number of starting tokens
func getStartingTokenCount() int {
	return 8
}
