package agentcap

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"strings"

	"golang.org/x/crypto/hkdf"
)

const Prefix = "AJZ1"

// Descriptor 智能体解密后才能看到的请求契约（前端不展示明文）。
type Descriptor struct {
	Version           string         `json:"v"`
	BaseURL           string         `json:"base_url"`
	AppSlug           string         `json:"app_slug"`
	TenantHint        string         `json:"tenant_hint,omitempty"`
	SchemaName        string         `json:"schema_name,omitempty"`
	BlueprintRevision string         `json:"blueprint_revision,omitempty"`
	Auth              AuthSpec       `json:"auth"`
	Resources         []ResourceSpec `json:"resources"`
	Notes             string         `json:"notes,omitempty"`
}

type AuthSpec struct {
	Type   string `json:"type"` // bearer_jwt
	Header string `json:"header"`
}

type ResourceSpec struct {
	Name       string      `json:"name"`
	Path       string      `json:"path"`
	Methods    []string    `json:"methods"`
	Filters    []string    `json:"filters,omitempty"`
	Sorts      []string    `json:"sorts,omitempty"`
	Fields     []FieldSpec `json:"fields,omitempty"`
	PrimaryKey string      `json:"primary_key"`
	Table      string      `json:"table,omitempty"`
}

type FieldSpec struct {
	Name string `json:"name"`
	Type string `json:"type"`
}

func DeriveKey(masterSecret string, tenantID int64, userID int64) []byte {
	h := hkdf.New(sha256.New, []byte(masterSecret), []byte("aijianzhan-agent-v1"), []byte(fmt.Sprintf("%d:%d", tenantID, userID)))
	key := make([]byte, 32)
	_, _ = io.ReadFull(h, key)
	return key
}

func Encrypt(key []byte, desc *Descriptor) (string, error) {
	raw, err := json.Marshal(desc)
	if err != nil {
		return "", err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}
	ciphertext := gcm.Seal(nil, nonce, raw, nil)
	return fmt.Sprintf("%s.%s.%s",
		Prefix,
		base64.RawURLEncoding.EncodeToString(nonce),
		base64.RawURLEncoding.EncodeToString(ciphertext),
	), nil
}

func Decrypt(key []byte, capsule string) (*Descriptor, error) {
	parts := strings.Split(capsule, ".")
	if len(parts) != 3 || parts[0] != Prefix {
		return nil, fmt.Errorf("invalid capsule format")
	}
	nonce, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return nil, err
	}
	ciphertext, err := base64.RawURLEncoding.DecodeString(parts[2])
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}
	plain, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return nil, fmt.Errorf("decrypt failed")
	}
	var desc Descriptor
	if err := json.Unmarshal(plain, &desc); err != nil {
		return nil, err
	}
	return &desc, nil
}

func PublicAgentKey(masterSecret string, tenantID, userID int64) string {
	key := DeriveKey(masterSecret, tenantID, userID)
	return base64.RawURLEncoding.EncodeToString(key)
}

func ParseAgentKey(b64 string) ([]byte, error) {
	return base64.RawURLEncoding.DecodeString(b64)
}
