package core

import (
	"errors"
	"fmt"
)

type InMemoryDb struct {
	Customers map[CustomerId]Customer
	Users     map[UserId]User
	userIdMap map[string]UserId
}

func NewInMemoryDb() InMemoryDb {
	return InMemoryDb{
		Customers: make(map[CustomerId]Customer),
		Users:     make(map[UserId]User),
		userIdMap: make(map[string]UserId),
	}
}

func (db *InMemoryDb) GetCustomer(id CustomerId) (*Customer, error) {
	customer, exists := db.Customers[id]
	if !exists {
		return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id))
	}
	return &customer, nil
}

func (db *InMemoryDb) GetUser(username UserEmail, customerId CustomerId) (*User, error) {
	key := userIdKey(customerId, username)
	userId, exists := db.userIdMap[key]
	if !exists {
		return nil, errors.New(fmt.Sprintf("customer %s with username %s dne", customerId, username))
	}
	user, exists := db.Users[userId]
	if !exists {
		panic(fmt.Sprintf("userId %s with customerId %s and username %s with no user", userId, customerId, username))
	}
	return &user, nil
}

func (db *InMemoryDb) WriteNewCustomer(customer Customer) error {
	_, exists := db.Customers[customer.Id]

	if exists {
		return errors.New(fmt.Sprintf("can write customer %s; already exists", customer.Id))
	}
	db.Customers[customer.Id] = customer
	return nil
}

func (db *InMemoryDb) WriteNewUser(user User) error {
	_, exists := db.Customers[user.CustomerId]
	if !exists {
		return errors.New(fmt.Sprintf("can't add user %s to customer %s: customer dne", user.Email, user.CustomerId))
	}
	userExists, _ := db.GetUser(user.Email, user.CustomerId)

	if userExists != nil {
		return errors.New(fmt.Sprintf("can't write new user %s, alread exists", user.Email))
	}
	key := userIdKey(user.CustomerId, user.Email)
	db.userIdMap[key] = user.Id
	db.Users[user.Id] = user
	return nil
}

func (db *InMemoryDb) UpdateUserNKode(user User) error {
	return errors.ErrUnsupported
}

func (db *InMemoryDb) UpdateUserInterface(userId UserId, ui UserInterface) error {
	user, exists := db.Users[userId]
	if !exists {
		return errors.New(fmt.Sprintf("can't update user %s, dne", user.Id))
	}
	user.Interface = ui
	db.Users[userId] = user
	return nil
}

func (db *InMemoryDb) UpdateUserRefreshToken(userId UserId, refreshToken string) error {
	return nil
}

func (db *InMemoryDb) Renew(id CustomerId) error {
	customer, exists := db.Customers[id]
	if !exists {
		return errors.New(fmt.Sprintf("customer %s does not exist", id))
	}
	setXor, attrsXor, err := customer.RenewKeys()
	if err != nil {
		return err
	}
	db.Customers[id] = customer
	for _, user := range db.Users {
		if user.CustomerId == id {
			err = user.RenewKeys(setXor, attrsXor)
			if err != nil {
				return err
			}
			db.Users[user.Id] = user
		}
	}
	return nil
}

func (db *InMemoryDb) RefreshUserPasscode(user User, passocode []int, customerAttr CustomerAttributes) error {
	err := user.RefreshPasscode(passocode, customerAttr)
	if err != nil {
		return err
	}
	db.Users[user.Id] = user
	return nil
}

func (db *InMemoryDb) RandomSvgInterface(kp KeypadDimension) ([]string, error) {
	return make([]string, kp.TotalAttrs()), nil
}

func (db *InMemoryDb) RandomSvgIdxInterface(kp KeypadDimension) (SvgIdInterface, error) {
	svgs := make(SvgIdInterface, kp.TotalAttrs())
	for idx := range svgs {
		svgs[idx] = idx
	}
	return svgs, nil
}

func (db *InMemoryDb) GetSvgStringInterface(idxs SvgIdInterface) ([]string, error) {
	return make([]string, len(idxs)), nil
}

func userIdKey(customerId CustomerId, username UserEmail) string {
	key := fmt.Sprintf("%s:%s", customerId, username)
	return key
}
