package auth

import (
	"errors"
	"html/template"
	"io"
	"time"

	"code.secondbit.org/uuid"
)

var (
	ErrNoTemplate = errors.New("no Template specified for the Context")
)

type Context struct {
	template *template.Template
	clients  ClientStore
	grants   GrantStore
	profiles ProfileStore
	tokens   TokenStore
}

func (c Context) Render(out io.Writer, name string, data interface{}) error {
	if c.template == nil {
		return ErrNoTemplate
	}
	return c.template.ExecuteTemplate(out, name, data)
}

func (c Context) GetClient(id uuid.ID) (Client, error) {
	if c.clients == nil {
		return Client{}, ErrNoClientStore
	}
	return c.clients.GetClient(id)
}

func (c Context) SaveClient(client Client) error {
	if c.clients == nil {
		return ErrNoClientStore
	}
	return c.clients.SaveClient(client)
}

func (c Context) UpdateClient(id uuid.ID, change ClientChange) error {
	if c.clients == nil {
		return ErrNoClientStore
	}
	return c.clients.UpdateClient(id, change)
}

func (c Context) DeleteClient(id uuid.ID) error {
	if c.clients == nil {
		return ErrNoClientStore
	}
	return c.clients.DeleteClient(id)
}

func (c Context) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
	if c.clients == nil {
		return []Client{}, ErrNoClientStore
	}
	return c.clients.ListClientsByOwner(ownerID, num, offset)
}

func (c Context) AddEndpoint(client uuid.ID, endpoint Endpoint) error {
	if c.clients == nil {
		return ErrNoClientStore
	}
	return c.clients.AddEndpoint(client, endpoint)
}

func (c Context) RemoveEndpoint(client, endpoint uuid.ID) error {
	if c.clients == nil {
		return ErrNoClientStore
	}
	return c.clients.RemoveEndpoint(client, endpoint)
}

func (c Context) CheckEndpoint(client uuid.ID, URI string) (bool, error) {
	if c.clients == nil {
		return false, ErrNoClientStore
	}
	return c.clients.CheckEndpoint(client, URI)
}

func (c Context) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) {
	if c.clients == nil {
		return []Endpoint{}, ErrNoClientStore
	}
	return c.clients.ListEndpoints(client, num, offset)
}

func (c Context) GetGrant(code string) (Grant, error) {
	if c.grants == nil {
		return Grant{}, ErrNoGrantStore
	}
	return c.grants.GetGrant(code)
}

func (c Context) SaveGrant(grant Grant) error {
	if c.grants == nil {
		return ErrNoGrantStore
	}
	return c.grants.SaveGrant(grant)
}

func (c Context) DeleteGrant(code string) error {
	if c.grants == nil {
		return ErrNoGrantStore
	}
	return c.grants.DeleteGrant(code)
}

func (c Context) GetProfileByID(id uuid.ID) (Profile, error) {
	if c.profiles == nil {
		return Profile{}, ErrNoProfileStore
	}
	return c.profiles.GetProfileByID(id)
}

func (c Context) GetProfileByLogin(loginType, value string) (Profile, error) {
	if c.profiles == nil {
		return Profile{}, ErrNoProfileStore
	}
	return c.profiles.GetProfileByLogin(loginType, value)
}

func (c Context) SaveProfile(profile Profile) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.SaveProfile(profile)
}

func (c Context) UpdateProfile(id uuid.ID, change ProfileChange) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.UpdateProfile(id, change)
}

func (c Context) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.UpdateProfiles(ids, change)
}

func (c Context) DeleteProfile(id uuid.ID) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.DeleteProfile(id)
}

func (c Context) AddLogin(login Login) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.AddLogin(login)
}

func (c Context) RemoveLogin(loginType, value string, profile uuid.ID) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.RemoveLogin(loginType, value, profile)
}

func (c Context) RecordLoginUse(loginType, value string, when time.Time) error {
	if c.profiles == nil {
		return ErrNoProfileStore
	}
	return c.profiles.RecordLoginUse(loginType, value, when)
}

func (c Context) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) {
	if c.profiles == nil {
		return []Login{}, ErrNoProfileStore
	}
	return c.profiles.ListLogins(profile, num, offset)
}

func (c Context) GetToken(token string, refresh bool) (Token, error) {
	if c.tokens == nil {
		return Token{}, ErrNoTokenStore
	}
	return c.tokens.GetToken(token, refresh)
}

func (c Context) SaveToken(token Token) error {
	if c.tokens == nil {
		return ErrNoTokenStore
	}
	return c.tokens.SaveToken(token)
}

func (c Context) RemoveToken(token string) error {
	if c.tokens == nil {
		return ErrNoTokenStore
	}
	return c.tokens.RemoveToken(token)
}

func (c Context) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
	if c.tokens == nil {
		return []Token{}, ErrNoTokenStore
	}
	return c.tokens.GetTokensByProfileID(profileID, num, offset)
}
