package apikeygenprefix

import (
	"encoding/hex"
	"hash/crc32"
	"strings"

	"github.com/grafana/grafana/pkg/util"
)

const GrafanaPrefix = "gl"

type KeyGenResult struct {
	HashedKey    string
	ClientSecret string
}

type PrefixedKey struct {
	ServiceID string
	Secret    string
	Checksum  string
}

func (p *PrefixedKey) Hash() (string, error) {
	hash, err := util.EncodePassword(p.Secret, p.Checksum)
	if err != nil {
		return "", err
	}
	return hash, nil
}

func (p *PrefixedKey) key() string {
	return GrafanaPrefix + p.ServiceID + "_" + p.Secret
}

func (p *PrefixedKey) CalculateChecksum() string {
	checksum := crc32.ChecksumIEEE([]byte(p.key()))
	//checksum to []byte
	checksumBytes := make([]byte, 4)
	checksumBytes[0] = byte(checksum)
	checksumBytes[1] = byte(checksum >> 8)
	checksumBytes[2] = byte(checksum >> 16)
	checksumBytes[3] = byte(checksum >> 24)

	return hex.EncodeToString(checksumBytes)
}

func (p *PrefixedKey) String() string {
	return p.key() + "_" + p.Checksum
}

func New(serviceID string) (KeyGenResult, error) {
	result := KeyGenResult{}

	secret, err := util.GetRandomString(32)
	if err != nil {
		return result, err
	}

	key := PrefixedKey{ServiceID: serviceID, Secret: secret, Checksum: ""}
	key.Checksum = key.CalculateChecksum()

	result.HashedKey, err = key.Hash()
	if err != nil {
		return result, err
	}

	result.ClientSecret = key.String()

	return result, nil
}

func Decode(keyString string) (*PrefixedKey, error) {
	if !strings.HasPrefix(keyString, GrafanaPrefix) {
		return nil, &ErrInvalidApiKey{}
	}

	parts := strings.Split(keyString, "_")
	if len(parts) != 3 {
		return nil, &ErrInvalidApiKey{}
	}

	key := &PrefixedKey{
		ServiceID: strings.TrimPrefix(parts[0], GrafanaPrefix),
		Secret:    parts[1],
		Checksum:  parts[2],
	}
	if key.CalculateChecksum() != key.Checksum {
		return nil, &ErrInvalidApiKey{}
	}

	return key, nil
}
