package htpasswd

import (
	"bytes"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
)

// Ssha facilitates ssha style hashing
type Ssha struct{}

// Hash returns the hashed variant of the password or an error
func (ss *Ssha) Hash(passwd string) (string, error) {
	s := sha1.New()
	_, err := s.Write([]byte(passwd))
	if err != nil {
		return "", err
	}
	salt := make([]byte, 4)
	_, err = rand.Read(salt)
	if err != nil {
		return "", err
	}
	_, err = s.Write([]byte(salt))
	if err != nil {
		return "", err
	}
	passwordSum := []byte(s.Sum(nil))
	ret := append(passwordSum, salt...)
	return ss.Prefix() + base64.StdEncoding.EncodeToString(ret), nil
}

// Match verifier the hashed password using the original
func (*Ssha) Match(password, hashedPassword string) error {
	eppS := hashedPassword[6:]
	hash, err := base64.StdEncoding.DecodeString(eppS)

	if err != nil {
		return fmt.Errorf("cannot base64 decode")
	}

	salt := hash[len(hash)-4:]
	sha := sha1.New()
	_, err = sha.Write([]byte(password))

	if err != nil {
		return err
	}

	_, err = sha.Write(salt)

	if err != nil {
		return err
	}

	sum := sha.Sum(nil)

	if !bytes.Equal(sum, hash[:len(hash)-4]) {
		return fmt.Errorf("wrong password")
	}

	return nil
}

// Name returns the name of the hasher
func (*Ssha) Name() string { return "ssha" }

// Prefix returns the hasher's prefix
func (*Ssha) Prefix() string { return "{SSHA}" }