package directory

import (
	"github.com/minio/sha256-simd"
	"encoding/hex"
	"net/url"
	"regexp"
	"strings"
	"time"

	"lol.mleku.dev/chk"
	"lol.mleku.dev/errorf"
	"git.mleku.dev/mleku/nostr/crypto/ec/schnorr"
	"git.mleku.dev/mleku/nostr/crypto/ec/secp256k1"
	"git.mleku.dev/mleku/nostr/encoders/bech32encoding"
	"git.mleku.dev/mleku/nostr/encoders/event"
)

// Validation constants
const (
	MaxKeyDelegations = 512
	KeyExpirationDays = 30
	MinNonceSize      = 16    // bytes
	MaxContentLength  = 65536 // bytes
)

// Regular expressions for validation
var (
	hexKeyRegex       = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)
	npubRegex         = regexp.MustCompile(`^npub1[0-9a-z]+$`)
	wsURLRegex        = regexp.MustCompile(`^wss?://[a-zA-Z0-9.-]+(?::[0-9]+)?(?:/.*)?$`)
	groupTagNameRegex = regexp.MustCompile(`^[a-zA-Z0-9._~-]+$`) // RFC 3986 URL-safe characters
)

// ValidateGroupTagName validates that a group tag name is URL-safe (RFC 3986).
func ValidateGroupTagName(name string) (err error) {
	if len(name) < 1 {
		return errorf.E("group tag name cannot be empty")
	}
	if len(name) > 255 {
		return errorf.E("group tag name cannot exceed 255 characters")
	}

	// Check for reserved prefixes
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") {
		return errorf.E("group tag names starting with '.' or '_' are reserved for system use")
	}

	// Validate URL-safe character set
	if !groupTagNameRegex.MatchString(name) {
		return errorf.E("group tag name must contain only URL-safe characters (a-z, A-Z, 0-9, -, ., _, ~)")
	}

	return nil
}

// ValidateHexKey validates that a string is a valid 64-character hex key.
func ValidateHexKey(key string) (err error) {
	if !hexKeyRegex.MatchString(key) {
		return errorf.E("invalid hex key format: must be 64 hex characters")
	}
	return nil
}

// ValidateNPub validates that a string is a valid npub-encoded public key.
func ValidateNPub(npub string) (err error) {
	if !npubRegex.MatchString(npub) {
		return errorf.E("invalid npub format")
	}

	// Try to decode to verify it's valid
	if _, err = bech32encoding.NpubToBytes(npub); chk.E(err) {
		return errorf.E("invalid npub encoding: %w", err)
	}

	return nil
}

// ValidateWebSocketURL validates that a string is a valid WebSocket URL.
func ValidateWebSocketURL(wsURL string) (err error) {
	if !wsURLRegex.MatchString(wsURL) {
		return errorf.E("invalid WebSocket URL format")
	}

	// Parse URL for additional validation
	var u *url.URL
	if u, err = url.Parse(wsURL); chk.E(err) {
		return errorf.E("invalid URL: %w", err)
	}

	if u.Scheme != "ws" && u.Scheme != "wss" {
		return errorf.E("URL must use ws:// or wss:// scheme")
	}

	if u.Host == "" {
		return errorf.E("URL must have a host")
	}

	return nil
}

// ValidateNonce validates that a nonce meets minimum security requirements.
func ValidateNonce(nonce string) (err error) {
	if len(nonce) < MinNonceSize*2 { // hex-encoded, so double the byte length
		return errorf.E("nonce must be at least %d bytes (%d hex characters)",
			MinNonceSize, MinNonceSize*2)
	}

	// Verify it's valid hex
	if _, err = hex.DecodeString(nonce); chk.E(err) {
		return errorf.E("nonce must be valid hex: %w", err)
	}

	return nil
}

// ValidateSignature validates that a signature is properly formatted.
func ValidateSignature(sig string) (err error) {
	if len(sig) != 128 { // 64 bytes hex-encoded
		return errorf.E("signature must be 64 bytes (128 hex characters)")
	}

	// Verify it's valid hex
	if _, err = hex.DecodeString(sig); chk.E(err) {
		return errorf.E("signature must be valid hex: %w", err)
	}

	return nil
}

// ValidateDerivationPath validates a BIP32 derivation path for this protocol.
func ValidateDerivationPath(path string) (err error) {
	// Expected format: m/39103'/1237'/identity'/usage/index
	if !strings.HasPrefix(path, "m/39103'/1237'/") {
		return errorf.E("derivation path must start with m/39103'/1237'/")
	}

	parts := strings.Split(path, "/")
	if len(parts) != 6 {
		return errorf.E("derivation path must have 6 components")
	}

	// Validate hardened components
	if parts[1] != "39103'" || parts[2] != "1237'" {
		return errorf.E("invalid hardened components in derivation path")
	}

	// Identity component should be hardened (end with ')
	if !strings.HasSuffix(parts[3], "'") {
		return errorf.E("identity component must be hardened")
	}

	return nil
}

// ValidateEventContent validates that event content is within size limits.
func ValidateEventContent(content []byte) (err error) {
	if len(content) > MaxContentLength {
		return errorf.E("content exceeds maximum length of %d bytes", MaxContentLength)
	}
	return nil
}

// ValidateTimestamp validates that a timestamp is reasonable (not too far in past/future).
func ValidateTimestamp(ts int64) (err error) {
	now := time.Now().Unix()

	// Allow up to 1 hour in the future
	if ts > now+3600 {
		return errorf.E("timestamp too far in the future")
	}

	// Allow up to 1 year in the past
	if ts < now-31536000 {
		return errorf.E("timestamp too far in the past")
	}

	return nil
}

// VerifyIdentityTagSignature verifies the signature in an identity tag.
func VerifyIdentityTagSignature(
	identityTag *IdentityTag,
	delegatePubkey []byte,
) (valid bool, err error) {
	if identityTag == nil {
		return false, errorf.E("identity tag cannot be nil")
	}

	// Decode npub to get identity public key
	var identityPubkey []byte
	if identityPubkey, err = bech32encoding.NpubToBytes(identityTag.NPubIdentity); chk.E(err) {
		return false, errorf.E("failed to decode npub: %w", err)
	}

	// Decode nonce and signature
	var nonce, signature []byte
	if nonce, err = hex.DecodeString(identityTag.Nonce); chk.E(err) {
		return false, errorf.E("invalid nonce hex: %w", err)
	}
	if signature, err = hex.DecodeString(identityTag.Signature); chk.E(err) {
		return false, errorf.E("invalid signature hex: %w", err)
	}

	// Create message to verify: nonce + delegate_pubkey_hex + identity_pubkey_hex
	message := make([]byte, 0, len(nonce)+64+64)
	message = append(message, nonce...)
	message = append(message, []byte(hex.EncodeToString(delegatePubkey))...)
	message = append(message, []byte(hex.EncodeToString(identityPubkey))...)

	// Hash the message
	hash := sha256.Sum256(message)

	// Parse signature and verify
	var sig *schnorr.Signature
	if sig, err = schnorr.ParseSignature(signature); chk.E(err) {
		return false, errorf.E("failed to parse signature: %w", err)
	}

	// Parse public key
	var pubKey *secp256k1.PublicKey
	if pubKey, err = schnorr.ParsePubKey(identityPubkey); chk.E(err) {
		return false, errorf.E("failed to parse public key: %w", err)
	}

	return sig.Verify(hash[:], pubKey), nil
}

// ValidateEventKindForReplication validates that an event kind is appropriate
// for replication in the directory consensus protocol.
func ValidateEventKindForReplication(kind uint16) (err error) {
	// Directory events are always valid
	if IsDirectoryEventKind(kind) {
		return nil
	}

	// Protocol events (39100-39105) should not be replicated as regular events
	if kind >= 39100 && kind <= 39105 {
		return errorf.E("protocol events should not be replicated as directory events")
	}

	// Ephemeral events (20000-29999) should not be stored
	if kind >= 20000 && kind <= 29999 {
		return errorf.E("ephemeral events should not be replicated")
	}

	return nil
}

// ValidateRelayIdentityBinding verifies that a relay identity announcement
// is properly bound to its network address through NIP-11 signature verification.
func ValidateRelayIdentityBinding(
	announcement *RelayIdentityAnnouncement,
	nip11Pubkey, nip11Nonce, nip11Sig, relayAddress string,
) (valid bool, err error) {
	if announcement == nil {
		return false, errorf.E("announcement cannot be nil")
	}

	// Verify the announcement event pubkey matches the NIP-11 pubkey
	announcementPubkeyHex := hex.EncodeToString(announcement.Event.Pubkey)
	if announcementPubkeyHex != nip11Pubkey {
		return false, errorf.E("announcement pubkey does not match NIP-11 pubkey")
	}

	// Verify NIP-11 signature format
	if err = ValidateHexKey(nip11Pubkey); chk.E(err) {
		return false, errorf.E("invalid NIP-11 pubkey: %w", err)
	}
	if err = ValidateNonce(nip11Nonce); chk.E(err) {
		return false, errorf.E("invalid NIP-11 nonce: %w", err)
	}
	if err = ValidateSignature(nip11Sig); chk.E(err) {
		return false, errorf.E("invalid NIP-11 signature: %w", err)
	}

	// Decode components
	var pubkey, signature []byte
	if pubkey, err = hex.DecodeString(nip11Pubkey); chk.E(err) {
		return false, errorf.E("failed to decode NIP-11 pubkey: %w", err)
	}
	if signature, err = hex.DecodeString(nip11Sig); chk.E(err) {
		return false, errorf.E("failed to decode NIP-11 signature: %w", err)
	}

	// Create message: pubkey + nonce + relay_address
	message := nip11Pubkey + nip11Nonce + relayAddress
	hash := sha256.Sum256([]byte(message))

	// Parse signature and verify
	var sig *schnorr.Signature
	if sig, err = schnorr.ParseSignature(signature); chk.E(err) {
		return false, errorf.E("failed to parse signature: %w", err)
	}

	// Parse public key
	var pubKey *secp256k1.PublicKey
	if pubKey, err = schnorr.ParsePubKey(pubkey); chk.E(err) {
		return false, errorf.E("failed to parse public key: %w", err)
	}

	return sig.Verify(hash[:], pubKey), nil
}

// ValidateConsortiumEvent performs comprehensive validation of any consortium
// protocol event, including signature verification and protocol-specific checks.
func ValidateConsortiumEvent(ev *event.E) (err error) {
	if ev == nil {
		return errorf.E("event cannot be nil")
	}

	// Verify basic event signature
	if _, err = ev.Verify(); chk.E(err) {
		return errorf.E("invalid event signature: %w", err)
	}

	// Validate timestamp
	if err = ValidateTimestamp(ev.CreatedAt); chk.E(err) {
		return errorf.E("invalid timestamp: %w", err)
	}

	// Validate content size
	if err = ValidateEventContent(ev.Content); chk.E(err) {
		return errorf.E("invalid content: %w", err)
	}

	// Protocol-specific validation based on event kind
	switch ev.Kind {
	case RelayIdentityAnnouncementKind.K:
		var ria *RelayIdentityAnnouncement
		if ria, err = ParseRelayIdentityAnnouncement(ev); chk.E(err) {
			return errorf.E("failed to parse relay identity announcement: %w", err)
		}
		return ria.Validate()

	case TrustActKind.K:
		var ta *TrustAct
		if ta, err = ParseTrustAct(ev); chk.E(err) {
			return errorf.E("failed to parse trust act: %w", err)
		}
		return ta.Validate()

	case GroupTagActKind.K:
		var gta *GroupTagAct
		if gta, err = ParseGroupTagAct(ev); chk.E(err) {
			return errorf.E("failed to parse group tag act: %w", err)
		}
		return gta.Validate()

	case PublicKeyAdvertisementKind.K:
		var pka *PublicKeyAdvertisement
		if pka, err = ParsePublicKeyAdvertisement(ev); chk.E(err) {
			return errorf.E("failed to parse public key advertisement: %w", err)
		}
		return pka.Validate()

	case DirectoryEventReplicationRequestKind.K:
		var derr *DirectoryEventReplicationRequest
		if derr, err = ParseDirectoryEventReplicationRequest(ev); chk.E(err) {
			return errorf.E("failed to parse replication request: %w", err)
		}
		return derr.Validate()

	case DirectoryEventReplicationResponseKind.K:
		var derr *DirectoryEventReplicationResponse
		if derr, err = ParseDirectoryEventReplicationResponse(ev); chk.E(err) {
			return errorf.E("failed to parse replication response: %w", err)
		}
		return derr.Validate()

	default:
		return errorf.E("unknown consortium event kind: %d", ev.Kind)
	}
}

// IsConsortiumEvent returns true if the event is a consortium protocol event.
func IsConsortiumEvent(ev *event.E) bool {
	if ev == nil {
		return false
	}
	return ev.Kind >= 39100 && ev.Kind <= 39105
}
