// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation
// for decentralized authorization and capability delegation in the Sonr network.
// This package handles JWT-based tokens, cryptographic verification, and resource capabilities.
package ucan

import (
	"context"
	"crypto/sha256"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/ipfs/go-cid"
	"github.com/multiformats/go-multihash"
	"github.com/sonr-io/crypto/keys"
	"github.com/sonr-io/crypto/mpc"
)

// MPCSigningMethod implements JWT signing using MPC enclaves
type MPCSigningMethod struct {
	Name    string
	enclave mpc.Enclave
}

// NewMPCSigningMethod creates a new MPC-based JWT signing method
func NewMPCSigningMethod(name string, enclave mpc.Enclave) *MPCSigningMethod {
	return &MPCSigningMethod{
		Name:    name,
		enclave: enclave,
	}
}

// Alg returns the signing method algorithm name
func (m *MPCSigningMethod) Alg() string {
	return m.Name
}

// Verify verifies a JWT signature using the MPC enclave
func (m *MPCSigningMethod) Verify(signingString string, signature []byte, key any) error {
	// signature is already decoded bytes
	sig := signature

	// Hash the signing string
	hasher := sha256.New()
	hasher.Write([]byte(signingString))
	digest := hasher.Sum(nil)

	// Use MPC enclave to verify signature
	valid, err := m.enclave.Verify(digest, sig)
	if err != nil {
		return fmt.Errorf("failed to verify signature: %w", err)
	}

	if !valid {
		return fmt.Errorf("signature verification failed")
	}

	return nil
}

// Sign signs a JWT string using the MPC enclave
func (m *MPCSigningMethod) Sign(signingString string, key any) ([]byte, error) {
	// Hash the signing string
	hasher := sha256.New()
	hasher.Write([]byte(signingString))
	digest := hasher.Sum(nil)

	// Use MPC enclave to sign the digest
	sig, err := m.enclave.Sign(digest)
	if err != nil {
		return nil, fmt.Errorf("failed to sign with MPC: %w", err)
	}

	return sig, nil
}

// MPCTokenBuilder creates UCAN tokens using MPC signing
type MPCTokenBuilder struct {
	enclave       mpc.Enclave
	issuerDID     string
	address       string
	signingMethod *MPCSigningMethod
}

// NewMPCTokenBuilder creates a new MPC-based UCAN token builder
func NewMPCTokenBuilder(enclave mpc.Enclave) (*MPCTokenBuilder, error) {
	if !enclave.IsValid() {
		return nil, fmt.Errorf("invalid MPC enclave provided")
	}

	// Derive issuer DID and address from enclave public key
	pubKeyBytes := enclave.PubKeyBytes()
	issuerDID, address := deriveIssuerDIDFromBytes(pubKeyBytes)

	signingMethod := NewMPCSigningMethod("MPC256", enclave)

	return &MPCTokenBuilder{
		enclave:       enclave,
		issuerDID:     issuerDID,
		address:       address,
		signingMethod: signingMethod,
	}, nil
}

// GetIssuerDID returns the issuer DID derived from the enclave
func (b *MPCTokenBuilder) GetIssuerDID() string {
	return b.issuerDID
}

// GetAddress returns the address derived from the enclave
func (b *MPCTokenBuilder) GetAddress() string {
	return b.address
}

// CreateOriginToken creates a new origin UCAN token using MPC signing
func (b *MPCTokenBuilder) CreateOriginToken(
	audienceDID string,
	attenuations []Attenuation,
	facts []Fact,
	notBefore, expiresAt time.Time,
) (*Token, error) {
	return b.createToken(audienceDID, nil, attenuations, facts, notBefore, expiresAt)
}

// CreateDelegatedToken creates a delegated UCAN token using MPC signing
func (b *MPCTokenBuilder) CreateDelegatedToken(
	parent *Token,
	audienceDID string,
	attenuations []Attenuation,
	facts []Fact,
	notBefore, expiresAt time.Time,
) (*Token, error) {
	proofs, err := prepareDelegationProofs(parent, attenuations)
	if err != nil {
		return nil, err
	}

	return b.createToken(audienceDID, proofs, attenuations, facts, notBefore, expiresAt)
}

// createToken creates a UCAN token with MPC signing
func (b *MPCTokenBuilder) createToken(
	audienceDID string,
	proofs []Proof,
	attenuations []Attenuation,
	facts []Fact,
	notBefore, expiresAt time.Time,
) (*Token, error) {
	// Validate inputs
	if !isValidDID(audienceDID) {
		return nil, fmt.Errorf("invalid audience DID format: %s", audienceDID)
	}
	if len(attenuations) == 0 {
		return nil, fmt.Errorf("at least one attenuation is required")
	}

	// Create JWT token with MPC signing method
	token := jwt.New(b.signingMethod)

	// Set UCAN version in header
	token.Header["ucv"] = "0.9.0"

	// Prepare time claims
	var nbfUnix, expUnix int64
	if !notBefore.IsZero() {
		nbfUnix = notBefore.Unix()
	}
	if !expiresAt.IsZero() {
		expUnix = expiresAt.Unix()
	}

	// Convert attenuations to claim format
	attClaims := make([]map[string]any, len(attenuations))
	for i, att := range attenuations {
		attClaims[i] = map[string]any{
			"can":  att.Capability.GetActions(),
			"with": att.Resource.GetURI(),
		}
	}

	// Convert proofs to strings
	proofStrings := make([]string, len(proofs))
	for i, proof := range proofs {
		proofStrings[i] = string(proof)
	}

	// Convert facts to any slice
	factData := make([]any, len(facts))
	for i, fact := range facts {
		// Facts are stored as raw JSON, convert to any
		factData[i] = string(fact.Data)
	}

	// Set claims
	claims := jwt.MapClaims{
		"iss": b.issuerDID,
		"aud": audienceDID,
		"att": attClaims,
	}

	if nbfUnix > 0 {
		claims["nbf"] = nbfUnix
	}
	if expUnix > 0 {
		claims["exp"] = expUnix
	}
	if len(proofStrings) > 0 {
		claims["prf"] = proofStrings
	}
	if len(factData) > 0 {
		claims["fct"] = factData
	}

	token.Claims = claims

	// Sign the token using MPC enclave (key parameter is ignored for MPC signing)
	tokenString, err := token.SignedString(nil)
	if err != nil {
		return nil, fmt.Errorf("failed to sign token with MPC: %w", err)
	}

	return &Token{
		Raw:          tokenString,
		Issuer:       b.issuerDID,
		Audience:     audienceDID,
		ExpiresAt:    expUnix,
		NotBefore:    nbfUnix,
		Attenuations: attenuations,
		Proofs:       proofs,
		Facts:        facts,
	}, nil
}

// CreateVaultCapabilityToken creates a vault-specific UCAN token
func (b *MPCTokenBuilder) CreateVaultCapabilityToken(
	audienceDID string,
	vaultAddress string,
	enclaveDataCID string,
	actions []string,
	expiresAt time.Time,
) (*Token, error) {
	// Create vault-specific attenuation
	attenuation := CreateVaultAttenuation(actions, enclaveDataCID, vaultAddress)

	return b.CreateOriginToken(
		audienceDID,
		[]Attenuation{attenuation},
		nil,
		time.Time{}, // No not-before restriction
		expiresAt,
	)
}

// MPCDIDResolver resolves DIDs with special handling for MPC-derived DIDs
type MPCDIDResolver struct {
	enclave   mpc.Enclave
	issuerDID string
	fallback  DIDResolver
}

// NewMPCDIDResolver creates a new MPC DID resolver
func NewMPCDIDResolver(enclave mpc.Enclave, fallback DIDResolver) *MPCDIDResolver {
	pubKeyBytes := enclave.PubKeyBytes()
	issuerDID, _ := deriveIssuerDIDFromBytes(pubKeyBytes)

	return &MPCDIDResolver{
		enclave:   enclave,
		issuerDID: issuerDID,
		fallback:  fallback,
	}
}

// ResolveDIDKey resolves DID keys with MPC enclave support
func (r *MPCDIDResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) {
	// Check if this is the MPC-derived DID
	if didStr == r.issuerDID {
		return r.createDIDFromEnclave()
	}

	// Fall back to standard DID resolution
	if r.fallback != nil {
		return r.fallback.ResolveDIDKey(ctx, didStr)
	}

	// Default fallback to string parsing
	return keys.Parse(didStr)
}

// createDIDFromEnclave creates a DID from the MPC enclave's public key
func (r *MPCDIDResolver) createDIDFromEnclave() (keys.DID, error) {
	// This would need to be implemented based on how MPC public keys
	// are converted to the keys.DID format
	// For now, parse from the derived DID string
	return keys.Parse(r.issuerDID)
}

// MPCVerifier provides UCAN verification with MPC support
type MPCVerifier struct {
	*Verifier
	enclave mpc.Enclave
}

// NewMPCVerifier creates a UCAN verifier with MPC support
func NewMPCVerifier(enclave mpc.Enclave) *MPCVerifier {
	resolver := NewMPCDIDResolver(enclave, StringDIDResolver{})
	verifier := NewVerifier(resolver)

	return &MPCVerifier{
		Verifier: verifier,
		enclave:  enclave,
	}
}

// VerifyMPCToken verifies a UCAN token that may be signed with MPC
func (v *MPCVerifier) VerifyMPCToken(ctx context.Context, tokenString string) (*Token, error) {
	// Try standard verification first
	token, err := v.VerifyToken(ctx, tokenString)
	if err == nil {
		return token, nil
	}

	// If standard verification fails, try MPC-specific verification
	return v.verifyWithMPC(ctx, tokenString)
}

// verifyWithMPC attempts to verify using MPC signing method
func (v *MPCVerifier) verifyWithMPC(_ context.Context, tokenString string) (*Token, error) {
	// Create MPC signing method for verification
	mpcMethod := NewMPCSigningMethod("MPC256", v.enclave)

	// Parse with MPC method
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
		// Ensure the token uses MPC signing method
		if token.Method.Alg() != mpcMethod.Alg() {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Method)
		}
		// For MPC verification, the key is not used
		return nil, nil
	})
	if err != nil {
		return nil, fmt.Errorf("MPC token verification failed: %w", err)
	}

	// Extract and parse claims
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return nil, fmt.Errorf("invalid token claims type")
	}

	ucanToken, err := v.parseUCANClaims(claims, tokenString)
	if err != nil {
		return nil, fmt.Errorf("failed to parse UCAN claims: %w", err)
	}

	return ucanToken, nil
}

// MPCTokenValidator provides comprehensive UCAN token validation with MPC support
type MPCTokenValidator struct {
	*MPCVerifier
	enclaveValidation bool
}

// NewMPCTokenValidator creates a comprehensive UCAN token validator with MPC support
func NewMPCTokenValidator(enclave mpc.Enclave, enableEnclaveValidation bool) *MPCTokenValidator {
	verifier := NewMPCVerifier(enclave)
	return &MPCTokenValidator{
		MPCVerifier:       verifier,
		enclaveValidation: enableEnclaveValidation,
	}
}

// ValidateTokenForVaultOperation performs comprehensive validation for vault operations
func (v *MPCTokenValidator) ValidateTokenForVaultOperation(
	ctx context.Context,
	tokenString string,
	enclaveDataCID string,
	requiredAction string,
	vaultAddress string,
) (*Token, error) {
	// Step 1: Verify token signature and structure
	token, err := v.VerifyMPCToken(ctx, tokenString)
	if err != nil {
		return nil, fmt.Errorf("token verification failed: %w", err)
	}

	// Step 2: Validate vault-specific capability
	if err := ValidateVaultTokenCapability(token, enclaveDataCID, requiredAction); err != nil {
		return nil, fmt.Errorf("vault capability validation failed: %w", err)
	}

	// Step 3: Validate enclave data CID if enabled
	if v.enclaveValidation {
		if err := v.validateEnclaveDataCID(token, enclaveDataCID); err != nil {
			return nil, fmt.Errorf("enclave data validation failed: %w", err)
		}
	}

	// Step 4: Validate vault address if provided
	if vaultAddress != "" {
		if err := v.validateVaultAddress(token, vaultAddress); err != nil {
			return nil, fmt.Errorf("vault address validation failed: %w", err)
		}
	}

	// Step 5: Verify delegation chain if proofs exist
	if len(token.Proofs) > 0 {
		if err := v.VerifyDelegationChain(ctx, tokenString); err != nil {
			return nil, fmt.Errorf("delegation chain validation failed: %w", err)
		}
	}

	return token, nil
}

// ValidateTokenForResource validates token capabilities for a specific resource
func (v *MPCTokenValidator) ValidateTokenForResource(
	ctx context.Context,
	tokenString string,
	resourceURI string,
	requiredAbilities []string,
) (*Token, error) {
	token, err := v.VerifyCapability(ctx, tokenString, resourceURI, requiredAbilities)
	if err != nil {
		return nil, fmt.Errorf("capability verification failed: %w", err)
	}

	// Additional MPC-specific validation
	if v.enclaveValidation {
		if err := v.validateMPCIssuer(token); err != nil {
			return nil, fmt.Errorf("MPC issuer validation failed: %w", err)
		}
	}

	return token, nil
}

// validateEnclaveDataCID validates that the token contains the expected enclave data CID
func (v *MPCTokenValidator) validateEnclaveDataCID(token *Token, expectedCID string) error {
	tokenCID, err := GetEnclaveDataCID(token)
	if err != nil {
		return fmt.Errorf("failed to extract enclave data CID from token: %w", err)
	}

	if tokenCID != expectedCID {
		return fmt.Errorf("enclave data CID mismatch: token=%s, expected=%s", tokenCID, expectedCID)
	}

	return nil
}

// validateVaultAddress validates the vault address in token capabilities
func (v *MPCTokenValidator) validateVaultAddress(token *Token, expectedAddress string) error {
	for _, att := range token.Attenuations {
		if vaultCap, ok := att.Capability.(*VaultCapability); ok {
			if vaultCap.VaultAddress != "" && vaultCap.VaultAddress != expectedAddress {
				return fmt.Errorf("vault address mismatch: token=%s, expected=%s",
					vaultCap.VaultAddress, expectedAddress)
			}
		}
	}
	return nil
}

// validateMPCIssuer validates that the token issuer matches the MPC enclave
func (v *MPCTokenValidator) validateMPCIssuer(token *Token) error {
	expectedIssuer, _ := deriveIssuerDIDFromBytes(v.enclave.PubKeyBytes())

	if token.Issuer != expectedIssuer {
		return fmt.Errorf("token issuer does not match MPC enclave: token=%s, expected=%s",
			token.Issuer, expectedIssuer)
	}

	return nil
}

// createMPCVaultAttenuation creates MPC-specific vault attenuations
func createMPCVaultAttenuation(actions []string, enclaveDataCID, vaultAddress string) Attenuation {
	// Use the existing CreateVaultAttenuation function but add MPC-specific validation
	return CreateVaultAttenuation(actions, enclaveDataCID, vaultAddress)
}

// containsAdminAction checks if actions contain admin-level permissions
func containsAdminAction(actions []string) bool {
	adminActions := map[string]bool{
		"admin": true, "export": true, "import": true, "delete": true,
	}

	for _, action := range actions {
		if adminActions[action] {
			return true
		}
	}
	return false
}

// ValidateEnclaveDataIntegrity validates enclave data against IPFS CID
func ValidateEnclaveDataIntegrity(enclaveData *mpc.EnclaveData, expectedCID string) error {
	if enclaveData == nil {
		return fmt.Errorf("enclave data cannot be nil")
	}

	// Basic validation of enclave structure
	if len(enclaveData.PubBytes) == 0 {
		return fmt.Errorf("enclave public key bytes cannot be empty")
	}

	if enclaveData.PubHex == "" {
		return fmt.Errorf("enclave public key hex cannot be empty")
	}

	// Implement IPFS CID validation against enclave data hash
	// Serialize the enclave data for consistent hashing
	enclaveDataBytes, err := enclaveData.Marshal()
	if err != nil {
		return fmt.Errorf("failed to marshal enclave data: %w", err)
	}

	// 1. Hash the enclave data using SHA-256
	hasher := sha256.New()
	hasher.Write(enclaveDataBytes)
	digest := hasher.Sum(nil)

	// 2. Create multihash with SHA-256 prefix
	mhash, err := multihash.EncodeName(digest, "sha2-256")
	if err != nil {
		return fmt.Errorf("failed to create multihash: %w", err)
	}

	// 3. Create CID and compare with expected
	parsedExpectedCID, err := cid.Parse(expectedCID)
	if err != nil {
		return fmt.Errorf("failed to parse expected CID: %w", err)
	}

	// Create CID v1 with dag-pb codec (IPFS default)
	calculatedCID := cid.NewCidV1(cid.DagProtobuf, mhash)

	// Compare CIDs
	if !parsedExpectedCID.Equals(calculatedCID) {
		return fmt.Errorf(
			"CID verification failed: expected %s, calculated %s",
			parsedExpectedCID.String(),
			calculatedCID.String(),
		)
	}

	return nil
}

// MPCCapabilityBuilder helps build MPC-specific capabilities
type MPCCapabilityBuilder struct {
	enclave mpc.Enclave
	builder *MPCTokenBuilder
}

// NewMPCCapabilityBuilder creates a new MPC capability builder
func NewMPCCapabilityBuilder(enclave mpc.Enclave) (*MPCCapabilityBuilder, error) {
	builder, err := NewMPCTokenBuilder(enclave)
	if err != nil {
		return nil, fmt.Errorf("failed to create MPC token builder: %w", err)
	}

	return &MPCCapabilityBuilder{
		enclave: enclave,
		builder: builder,
	}, nil
}

// CreateVaultAdminCapability creates admin-level vault capabilities
func (b *MPCCapabilityBuilder) CreateVaultAdminCapability(
	vaultAddress, enclaveDataCID string,
) Attenuation {
	allActions := []string{"read", "write", "sign", "export", "import", "delete", "admin"}
	return CreateVaultAttenuation(allActions, enclaveDataCID, vaultAddress)
}

// CreateVaultReadOnlyCapability creates read-only vault capabilities
func (b *MPCCapabilityBuilder) CreateVaultReadOnlyCapability(
	vaultAddress, enclaveDataCID string,
) Attenuation {
	readActions := []string{"read"}
	return CreateVaultAttenuation(readActions, enclaveDataCID, vaultAddress)
}

// CreateVaultSigningCapability creates signing-specific vault capabilities
func (b *MPCCapabilityBuilder) CreateVaultSigningCapability(
	vaultAddress, enclaveDataCID string,
) Attenuation {
	signActions := []string{"read", "sign"}
	return CreateVaultAttenuation(signActions, enclaveDataCID, vaultAddress)
}

// CreateCustomCapability creates a custom capability with specified actions
func (b *MPCCapabilityBuilder) CreateCustomCapability(
	actions []string,
	vaultAddress, enclaveDataCID string,
) Attenuation {
	return CreateVaultAttenuation(actions, enclaveDataCID, vaultAddress)
}

// Utility functions

// deriveIssuerDIDFromBytes creates issuer DID and address from public key bytes
// Enhanced version using the crypto/keys package
func deriveIssuerDIDFromBytes(pubKeyBytes []byte) (string, string) {
	// Use the enhanced NewFromMPCPubKey method from crypto/keys
	did, err := keys.NewFromMPCPubKey(pubKeyBytes)
	if err != nil {
		// Fallback to simplified implementation
		address := fmt.Sprintf("addr_%x", pubKeyBytes[:8])
		issuerDID := fmt.Sprintf("did:sonr:%s", address)
		return issuerDID, address
	}

	// Use the proper DID generation and address derivation
	didStr := did.String()
	address, err := did.Address()
	if err != nil {
		// Fallback to simplified address
		address = fmt.Sprintf("addr_%x", pubKeyBytes[:8])
	}

	return didStr, address
}
