package protocol

import (
	"bytes"
	"crypto/sha512"
	"errors"
	"fmt"

	"filippo.io/edwards25519"
)

// Root Key (compressed Edwards25519 point)
var LicenseRootKeyBytes = [32]byte{
	0xcd, 0x0d, 0xe2, 0xae, 0xd4, 0x63, 0x45, 0x50, 0x9a, 0x7e, 0x3c,
	0xfd, 0x8f, 0x68, 0xb3, 0xdc, 0x75, 0x55, 0xb2, 0x9d, 0xcc, 0xec,
	0x73, 0xcd, 0x18, 0x75, 0x0f, 0x99, 0x38, 0x12, 0x40, 0x8a,
}

// ParseLicense and derive final Public Key
func ParseLicenseAndDeriveKey(licenseData []byte) ([]byte, error) {
	// Header
	if len(licenseData) < 1 || licenseData[0] != 0x01 {
		return nil, errors.New("invalid license version")
	}

	reader := bytes.NewReader(licenseData[1:])

	// Initialize current key with Root
	currentPoint, err := new(edwards25519.Point).SetBytes(LicenseRootKeyBytes[:])
	if err != nil {
		return nil, fmt.Errorf("failed to load root key: %v", err)
	}

	// Iterate blocks
	for reader.Len() > 0 {
		// Calculate Start Position
		bytesConsumed := (len(licenseData) - 1) - reader.Len()
		blockStartAbs := 1 + bytesConsumed

		// Read KeyType
		_, err = reader.ReadByte()
		if err != nil {
			return nil, fmt.Errorf("read keyType failed: %v", err)
		}

		// Read Public Key
		var pubKeyBytes [32]byte
		if _, err := reader.Read(pubKeyBytes[:]); err != nil {
			return nil, fmt.Errorf("read pubKey failed: %v", err)
		}

		blockPubKey, err := new(edwards25519.Point).SetBytes(pubKeyBytes[:])
		if err != nil {
			return nil, fmt.Errorf("invalid block public key: %v", err)
		}

		// Read Block Type
		blockType, err := reader.ReadByte()
		if err != nil {
			return nil, fmt.Errorf("read blockType failed: %v", err)
		}

		// Read Dates
		dates := make([]byte, 8)
		if _, err := reader.Read(dates); err != nil {
			return nil, fmt.Errorf("read dates failed: %v", err)
		}

		// Parse Content
		switch blockType {
		case 0x00: // Intermediate
			// 4 bytes int
			tmp := make([]byte, 4)
			if _, err := reader.Read(tmp); err != nil {
				return nil, fmt.Errorf("read intermediate int failed: %v", err)
			}
			// String
			if _, err := readNullTerminatedString(reader); err != nil {
				return nil, fmt.Errorf("read intermediate string failed: %v", err)
			}
		case 0x01, 0x03: // Website / Code
			if _, err := readNullTerminatedString(reader); err != nil {
				return nil, err
			}
		case 0x02: // Server
			// 5 bytes
			tmp := make([]byte, 5)
			if _, err := reader.Read(tmp); err != nil {
				return nil, fmt.Errorf("read server data failed: %v", err)
			}
			if _, err := readNullTerminatedString(reader); err != nil {
				return nil, fmt.Errorf("read server string failed: %v", err)
			}
		case 0x20: // Box
			// Ephemeral blocks might be empty content
			if reader.Len() > 0 {
				if _, err := readNullTerminatedString(reader); err != nil {
					return nil, err
				}
			}
		default:
			// Fallback
		}

		// Calculate End for Hashing
		bytesConsumedAfter := (len(licenseData) - 1) - reader.Len()
		blockEndAbs := 1 + bytesConsumedAfter
		hashStart := blockStartAbs + 1

		if blockEndAbs <= hashStart {
			return nil, errors.New("block too short for hashing")
		}

		hashableData := licenseData[hashStart:blockEndAbs]

		// Calculate SHA512 Hash
		hash := sha512.Sum512(hashableData)
		var scalarBytes [32]byte
		copy(scalarBytes[:], hash[:32]) // Take first 32 bytes

		// Clamp the hash
		scalarBytes[0] &= 248
		scalarBytes[31] &= 127
		scalarBytes[31] |= 64

		scalar, err := new(edwards25519.Scalar).SetBytesWithClamping(scalarBytes[:])
		if err != nil {
			return nil, fmt.Errorf("scalar creation failed: %v", err)
		}

		// Derive: current = current + (blockKey * hash)
		term := new(edwards25519.Point).ScalarMult(scalar, blockPubKey)
		currentPoint.Add(currentPoint, term)
	}

	return currentPoint.Bytes(), nil
}

func readNullTerminatedString(r *bytes.Reader) (string, error) {
	var data []byte
	for {
		b, err := r.ReadByte()
		if err != nil {
			return "", err
		}
		if b == 0x00 {
			break
		}
		data = append(data, b)
	}
	return string(data), nil
}
