package main

import (
	"errors"
	"math/rand"
	"time"
)

type Code struct {
	Code  string
	Valid time.Time
}

type Auth struct {
	codes map[string]Code

	mailer Mailer
}

var letterRunes = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

func randString(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func (a *Auth) CreateCode(email string) error {
	code := randString(10)

	if err := a.mailer.SendCode(email, code); err != nil {
		return err
	}

	a.codes[email] = Code{
		Code:  code,
		Valid: time.Now().Add(time.Minute),
	}

	return nil
}

func (a *Auth) IsCodeValid(email string, code string) bool {
	existingCode, exists := a.codes[email]
	if !exists {
		return false
	}

	return existingCode.Valid.After(time.Now()) && existingCode.Code == code
}

func (a *Auth) UseCode(email string, code string) error {
	if valid := a.IsCodeValid(email, code); !valid {
		return errors.New("This code is invalid.")
	}

	delete(a.codes, email)
	return nil
}

func CreateAuth(mailer Mailer) Auth {
	return Auth{
		codes:  make(map[string]Code),
		mailer: mailer,
	}
}
