package osin

import (
	"crypto/sha256"
	"fmt"
	"hash"

	"github.com/satori/go.uuid"
)

// Client information
type Client interface {
	// Client id
	GetID() string

	GetVersion() string

	GetSecretHash() (hash string)

	GetHashAlgorithm() string
	SetHashAlgorithm(algorithm string)

	// Compare secret
	CompareSecret(secret string) (ok bool)

	// Base client uri
	GetRedirectURI() string

	// Data to be passed to storage. Not used by the library.
	GetUserData() interface{}

	// Reset version
	NewVersion() (version string)

	// Reset client secret
	RegenerateSecret() (err error)
}

// GetHasher returns hash.Hash based on hash algorithm name
func GetHasher(hashAlg string) (hasher hash.Hash, err error) {
	switch hashAlg {
	case "sha256":
		hasher = sha256.New()
	default:
		hasher = nil
	}

	if hasher == nil {
		err = HashAlgNotImplementedError(hashAlg)
		return
	}
	return
}

// HashAlgNotImplementedError is returned when an unsupported hash algorithm is
// specified in config
type HashAlgNotImplementedError string

func (e HashAlgNotImplementedError) Error() string {
	return fmt.Sprintf("osin: hash algorithm %s not implemented", e)
}

// DefaultClient stores all data in struct variables
type DefaultClient struct {
	ID            string
	SecretHash    string
	RedirectURI   string
	Version       string
	HashAlgorithm string
	UserData      interface{}
}

func (d *DefaultClient) GetID() string {
	return d.ID
}

// NewVersion resets client version
func (d *DefaultClient) NewVersion() (version string) {
	version = uuid.NewV4().String()
	d.Version = version
	return
}

func (d *DefaultClient) SetHashAlgorithm(algorithm string) {
	d.HashAlgorithm = algorithm
	return
}

func (d *DefaultClient) GetHashAlgorithm() (algorithm string) {
	algorithm = d.HashAlgorithm
	return
}

// GetVersion returns client version
func (d *DefaultClient) GetVersion() (version string) {
	version = d.Version
	return
}

// RegenerateSecret regenerates secret
func (d *DefaultClient) RegenerateSecret() (err error) {
	return
}

func (d *DefaultClient) CompareSecret(secret string) (ok bool) {
	ok = false
	if secret == d.SecretHash {
		ok = true
	}
	return
}

func (d *DefaultClient) GetSecretHash() (hash string) {
	hash = d.SecretHash
	return
}

func (d *DefaultClient) GetRedirectURI() string {
	return d.RedirectURI
}

func (d *DefaultClient) GetUserData() interface{} {
	return d.UserData
}

func (d *DefaultClient) CopyFrom(client Client) {
	d.ID = client.GetID()
	d.SecretHash = client.GetSecretHash()
	d.RedirectURI = client.GetRedirectURI()
	d.UserData = client.GetUserData()
}
