package auth

import (
	"errors"
	"time"

	"secondbit.org/uuid"
)

var (
	ErrProfileAlreadyExists = errors.New("profile already exists in ProfileStore")
	ErrProfileNotFound      = errors.New("profile not found in ProfileStore")
)

type Profile struct {
	ID                     uuid.ID
	Name                   string
	Passphrase             string
	Iterations             int64
	Salt                   string
	PassphraseScheme       int
	Compromised            bool
	LockedUntil            time.Time
	PassphraseReset        string
	PassphraseResetCreated time.Time
	Created                time.Time
	LastSeen               time.Time
}

func (p *Profile) ApplyChange(change ProfileChange) {
	if change.Name != nil {
		p.Name = *change.Name
	}
	if change.Passphrase != nil {
		p.Passphrase = *change.Passphrase
	}
	if change.Iterations != nil {
		p.Iterations = *change.Iterations
	}
	if change.Salt != nil {
		p.Salt = *change.Salt
	}
	if change.PassphraseScheme != nil {
		p.PassphraseScheme = *change.PassphraseScheme
	}
	if change.Compromised != nil {
		p.Compromised = *change.Compromised
	}
	if change.LockedUntil != nil {
		p.LockedUntil = *change.LockedUntil
	}
	if change.PassphraseReset != nil {
		p.PassphraseReset = *change.PassphraseReset
	}
	if change.PassphraseResetCreated != nil {
		p.PassphraseResetCreated = *change.PassphraseResetCreated
	}
	if change.LastSeen != nil {
		p.LastSeen = *change.LastSeen
	}
}

type ProfileChange struct {
	Name                   *string
	Passphrase             *string
	Iterations             *int64
	Salt                   *string
	PassphraseScheme       *int
	Compromised            *bool
	LockedUntil            *time.Time
	PassphraseReset        *string
	PassphraseResetCreated *time.Time
	LastSeen               *time.Time
}

func (c ProfileChange) Validate() error {
	return nil
}

type Login struct {
	Type      string
	Value     string
	ProfileID uuid.ID
	Created   time.Time
	LastUsed  time.Time
}

type ProfileStore interface {
	GetProfileByID(id uuid.ID) (Profile, error)
	GetProfileByLogin(login Login) (Profile, error)
	SaveProfile(profile Profile) error
	UpdateProfile(id uuid.ID, change ProfileChange) error
	DeleteProfile(id uuid.ID) error
}

func (m *Memstore) GetProfileByID(id uuid.ID) (Profile, error) {
	m.profileLock.RLock()
	defer m.profileLock.RUnlock()
	p, ok := m.profiles[id.String()]
	if !ok {
		return Profile{}, ErrProfileNotFound
	}
	return p, nil
}

func (m *Memstore) GetProfileByLogin(login Login) (Profile, error) {
	return Profile{}, nil
}

func (m *Memstore) SaveProfile(profile Profile) error {
	m.profileLock.Lock()
	defer m.profileLock.Unlock()
	_, ok := m.profiles[profile.ID.String()]
	if ok {
		return ErrProfileAlreadyExists
	}
	m.profiles[profile.ID.String()] = profile
	return nil
}

func (m *Memstore) UpdateProfile(id uuid.ID, change ProfileChange) error {
	m.profileLock.Lock()
	defer m.profileLock.Unlock()
	p, ok := m.profiles[id.String()]
	if !ok {
		return ErrProfileNotFound
	}
	p.ApplyChange(change)
	m.profiles[id.String()] = p
	return nil
}

func (m *Memstore) DeleteProfile(id uuid.ID) error {
	m.profileLock.Lock()
	defer m.profileLock.Unlock()
	_, ok := m.profiles[id.String()]
	if !ok {
		return ErrProfileNotFound
	}
	delete(m.profiles, id.String())
	return nil
}
