package license

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base32"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"time"
)

var (
	ErrLicenseInvalid            = errors.New("license: invalid")
	ErrLicenseUnsupportedVersion = errors.New("license: unsupported version")
	ErrLicenseExpired            = errors.New("license: expired")
)

type EncryptVersion uint8

const (
	EncryptV1 EncryptVersion = 1
)

const (
	// EncryptDefault 默认加密版本
	EncryptDefault = EncryptV1
)

type Info interface {
	CreateAt() time.Time
	ExpireAt() time.Time
	Expired() bool
}

type InvalidLicense struct {
	Create time.Time
}

func (e *InvalidLicense) CreateAt() time.Time {
	if e.Create.IsZero() {
		e.Create = time.Now()
	}
	return e.Create
}

func (e *InvalidLicense) ExpireAt() time.Time {
	return e.Create
}

func (e *InvalidLicense) Expired() bool {
	return true
}

// License 密钥授权
type License struct {
	key []byte
}

// New 使用 expiration 作为过期时间创建一个密钥
// 创建时会注入[创建者]操作系统的时间到密钥中用于解密时的时间范围验证
func (l *License) New(expiration time.Time) (string, error) {
	if expiration.IsZero() {
		return "", fmt.Errorf("invalid expiration: %s", expiration)
	}
	plaintext, err := l.NewWith(EncryptDefault, expiration)
	if err != nil {
		return "", err
	}
	encrypted, err := l.Encrypt(plaintext)
	if err != nil {
		return "", err
	}
	return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(encrypted), nil
}

// Stat 解密 encryptedKey 并返回许可证信息
func (l *License) Stat(encryptedKey string) (Info, error) {
	encrypted, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(encryptedKey)
	if err != nil {
		return nil, err
	}
	plaintext, err := l.Decrypt(encrypted)
	if err != nil {
		return nil, err
	}
	return l.StatWith(EncryptDefault, plaintext)
}

// NewWith 与 New 相等, 但指定加密版本号
func (l *License) NewWith(v EncryptVersion, expiration time.Time) ([]byte, error) {
	switch v {
	case EncryptV1:
		return l.newV1(expiration)
	default:
		return nil, ErrLicenseUnsupportedVersion
	}
}

// StatWith 与 Stat 相等, 但指定加密版本号
func (l *License) StatWith(v EncryptVersion, plaintext []byte) (Info, error) {
	switch v {
	case EncryptV1:
		return l.statV1(plaintext)
	default:
		return nil, ErrLicenseUnsupportedVersion
	}
}

// Encrypt 使用 AES-128-GCM 加密 plaintext 并返回密文
func (l *License) Encrypt(plaintext []byte) ([]byte, error) {
	block, err := aes.NewCipher(l.key)
	if err != nil {
		return nil, err
	}
	gcm, err := cipher.NewGCMWithRandomNonce(block)
	if err != nil {
		return nil, err
	}
	// 生成随机 nonce
	nonce := make([]byte, gcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		return nil, err
	}
	ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
	return append(nonce, ciphertext...), nil
}

// Decrypt 使用 AES-128-GCM 解密 encrypted 并返回明文
func (l *License) Decrypt(encrypted []byte) ([]byte, error) {
	block, err := aes.NewCipher(l.key)
	if err != nil {
		return nil, err
	}
	// 创建 GCM 解密器
	gcm, err := cipher.NewGCMWithRandomNonce(block)
	if err != nil {
		return nil, err
	}
	// 提取 nonce
	nonceSize := gcm.NonceSize()
	if len(encrypted) < nonceSize {
		return nil, fmt.Errorf("invalid encrypted data")
	}
	nonce := encrypted[:nonceSize]
	ciphertext := encrypted[nonceSize:]
	// 使用 GCM 解密
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return nil, err
	}
	return plaintext, nil
}

// isValidDate 检查有效期
func (l *License) isValidDate(now, sub time.Time) bool {
	if now.After(sub) {
		return true // [当前时间] 大于 [密钥创建时间] 时有效
	}
	// [当前时间] 小于 [密钥创建时间] 并且误差在 1 小时内时有效
	return now.Sub(sub).Abs() < 1*time.Hour
}

// v1Info 实现 EncryptV1 版本的 Info
type v1Info struct {
	createdAt time.Time
	expiresAt time.Time
}

func (v *v1Info) CreateAt() time.Time {
	return v.createdAt
}

func (v *v1Info) ExpireAt() time.Time {
	return v.expiresAt
}

func (v *v1Info) Expired() bool {
	return time.Now().After(v.expiresAt)
}

// newV1 创建 EncryptV1 所需明文
func (l *License) newV1(expiration time.Time) ([]byte, error) {
	now := time.Now().Local().Format(time.RFC3339)
	exp := expiration.Local().Format(time.RFC3339)
	plaintext := []byte(fmt.Sprintf("1|%s|%s", now, exp))
	return plaintext, nil
}

// statV1 查看 newV1 创建的明文
func (l *License) statV1(plaintext []byte) (Info, error) {
	clips := bytes.Split(plaintext, []byte("|"))
	if len(clips) != 3 {
		return nil, ErrLicenseInvalid
	}
	ver, err := strconv.ParseUint(string(clips[0]), 10, 32)
	if err != nil {
		return nil, ErrLicenseInvalid
	}
	if ver != 1 {
		return nil, ErrLicenseInvalid
	}
	// 当前时间
	now := time.Now().Local()
	// 创建时间
	createAt, err := time.ParseInLocation(time.RFC3339, string(clips[1]), time.Local)
	if err != nil {
		return nil, ErrLicenseInvalid
	}
	if !l.isValidDate(now, createAt) {
		return nil, ErrLicenseInvalid
	}
	// 过期时间
	expAt, err := time.ParseInLocation(time.RFC3339, string(clips[2]), time.Local)
	if err != nil {
		return nil, ErrLicenseExpired
	}
	return &v1Info{
		createdAt: createAt,
		expiresAt: expAt,
	}, nil
}

var (
	// key = []byte("2c64d157-a68d-4150-b9a6-ef873f51")
	key = []byte("SIMANC-LEGENDARY") // key 16bit = AES-128
)

var (
	globalLicense = &License{key: key}
)

func New(expiration time.Time) (string, error) {
	return globalLicense.New(expiration)
}

func Stat(encryptedKey string) (Info, error) {
	return globalLicense.Stat(encryptedKey)
}

func Open(name string) (Info, error) {
	fi, err := os.ReadFile(name)
	if err != nil {
		return nil, err
	}
	return globalLicense.Stat(string(fi))
}
