package directory

import (
	"github.com/minio/sha256-simd"
	"encoding/hex"
	"fmt"
	"strconv"
	"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/interfaces/signer/p8k"
	"git.mleku.dev/mleku/nostr/encoders/bech32encoding"
	"git.mleku.dev/mleku/nostr/encoders/event"
)

// IdentityTagBuilder helps construct identity tags with proper signatures.
type IdentityTagBuilder struct {
	identityPrivkey []byte
	identityPubkey  []byte
	npubIdentity    string
}

// NewIdentityTagBuilder creates a new identity tag builder with the given
// identity private key.
func NewIdentityTagBuilder(identityPrivkey []byte) (builder *IdentityTagBuilder, err error) {
	if len(identityPrivkey) != 32 {
		return nil, errorf.E("identity private key must be 32 bytes")
	}

	// Derive public key from secret key using p8k signer
	var signer *p8k.Signer
	if signer, err = p8k.New(); chk.E(err) {
		return nil, errorf.E("failed to create signer: %w", err)
	}
	if err = signer.InitSec(identityPrivkey); chk.E(err) {
		return nil, errorf.E("failed to initialize signer: %w", err)
	}
	identityPubkeyBytes := signer.Pub()

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

	// Encode as npub
	var npubIdentity []byte
	if npubIdentity, err = bech32encoding.PublicKeyToNpub(identityPubkey); chk.E(err) {
		return nil, errorf.E("failed to encode npub: %w", err)
	}

	return &IdentityTagBuilder{
		identityPrivkey: identityPrivkey,
		identityPubkey:  identityPubkeyBytes,
		npubIdentity:    string(npubIdentity),
	}, nil
}

// CreateIdentityTag creates a signed identity tag for the given delegate pubkey.
func (builder *IdentityTagBuilder) CreateIdentityTag(delegatePubkey []byte) (identityTag *IdentityTag, err error) {
	if len(delegatePubkey) != 32 {
		return nil, errorf.E("delegate pubkey must be 32 bytes")
	}

	// Generate nonce
	var nonceHex string
	if nonceHex, err = GenerateNonceHex(16); chk.E(err) {
		return nil, errorf.E("failed to generate nonce: %w", err)
	}

	// Create message: nonce + delegate_pubkey_hex + identity_pubkey_hex
	delegatePubkeyHex := hex.EncodeToString(delegatePubkey)
	identityPubkeyHex := hex.EncodeToString(builder.identityPubkey)
	message := nonceHex + delegatePubkeyHex + identityPubkeyHex

	// Hash and sign using p8k signer
	hash := sha256.Sum256([]byte(message))
	var signer *p8k.Signer
	if signer, err = p8k.New(); chk.E(err) {
		return nil, errorf.E("failed to create signer: %w", err)
	}
	if err = signer.InitSec(builder.identityPrivkey); chk.E(err) {
		return nil, errorf.E("failed to initialize signer: %w", err)
	}
	var signature []byte
	if signature, err = signer.Sign(hash[:]); chk.E(err) {
		return nil, errorf.E("failed to sign identity tag: %w", err)
	}

	identityTag = &IdentityTag{
		NPubIdentity: builder.npubIdentity,
		Nonce:        nonceHex,
		Signature:    hex.EncodeToString(signature),
	}

	return
}

// GetNPubIdentity returns the npub-encoded identity.
func (builder *IdentityTagBuilder) GetNPubIdentity() string {
	return builder.npubIdentity
}

// GetIdentityPubkey returns the raw identity public key.
func (builder *IdentityTagBuilder) GetIdentityPubkey() []byte {
	return builder.identityPubkey
}

// KeyPoolManager helps manage HD key derivation and advertisement.
type KeyPoolManager struct {
	masterSeed     []byte
	identityIndex  uint32
	currentIndices map[KeyPurpose]int
}

// NewKeyPoolManager creates a new key pool manager with the given master seed.
func NewKeyPoolManager(masterSeed []byte, identityIndex uint32) *KeyPoolManager {
	return &KeyPoolManager{
		masterSeed:     masterSeed,
		identityIndex:  identityIndex,
		currentIndices: make(map[KeyPurpose]int),
	}
}

// GenerateDerivationPath creates a BIP32 derivation path for the given purpose and index.
func (kpm *KeyPoolManager) GenerateDerivationPath(purpose KeyPurpose, index int) string {
	var usageIndex int
	switch purpose {
	case KeyPurposeSigning:
		usageIndex = 0
	case KeyPurposeEncryption:
		usageIndex = 1
	case KeyPurposeDelegation:
		usageIndex = 2
	default:
		usageIndex = 0
	}

	return fmt.Sprintf("m/39103'/1237'/%d'/%d/%d", kpm.identityIndex, usageIndex, index)
}

// GetNextKeyIndex returns the next available key index for the given purpose.
func (kpm *KeyPoolManager) GetNextKeyIndex(purpose KeyPurpose) int {
	current := kpm.currentIndices[purpose]
	kpm.currentIndices[purpose] = current + 1
	return current
}

// SetKeyIndex sets the current key index for the given purpose.
func (kpm *KeyPoolManager) SetKeyIndex(purpose KeyPurpose, index int) {
	kpm.currentIndices[purpose] = index
}

// GetCurrentKeyIndex returns the current key index for the given purpose.
func (kpm *KeyPoolManager) GetCurrentKeyIndex(purpose KeyPurpose) int {
	return kpm.currentIndices[purpose]
}

// TrustCalculator helps calculate trust scores and inheritance.
type TrustCalculator struct {
	acts map[string]*TrustAct
}

// NewTrustCalculator creates a new trust calculator.
func NewTrustCalculator() *TrustCalculator {
	return &TrustCalculator{
		acts: make(map[string]*TrustAct),
	}
}

// AddAct adds a trust act to the calculator.
func (tc *TrustCalculator) AddAct(act *TrustAct) {
	key := act.GetTargetPubkey()
	tc.acts[key] = act
}

// GetTrustLevel returns the trust level for a given pubkey.
func (tc *TrustCalculator) GetTrustLevel(pubkey string) TrustLevel {
	if act, exists := tc.acts[pubkey]; exists {
		if !act.IsExpired() {
			return act.GetTrustLevel()
		}
	}
	return TrustLevelNone // Return 0 for no trust
}

// CalculateInheritedTrust calculates inherited trust through the web of trust.
// With numeric trust levels, inherited trust is calculated by multiplying
// the trust percentages at each hop, reducing trust over distance.
func (tc *TrustCalculator) CalculateInheritedTrust(
	fromPubkey, toPubkey string,
) TrustLevel {
	// Direct trust
	if directTrust := tc.GetTrustLevel(toPubkey); directTrust > 0 {
		return directTrust
	}

	// Look for inherited trust through intermediate nodes
	var maxInheritedTrust TrustLevel = 0
	for intermediatePubkey, act := range tc.acts {
		if act.IsExpired() {
			continue
		}

		// Check if we trust the intermediate node
		intermediateLevel := tc.GetTrustLevel(intermediatePubkey)
		if intermediateLevel == 0 {
			continue
		}

		// Check if intermediate node trusts the target
		targetLevel := tc.GetTrustLevel(toPubkey)
		if targetLevel == 0 {
			continue
		}

		// Calculate inherited trust level (multiply percentages)
		inheritedLevel := tc.combinesTrustLevels(intermediateLevel, targetLevel)
		if inheritedLevel > maxInheritedTrust {
			maxInheritedTrust = inheritedLevel
		}
	}

	return maxInheritedTrust
}

// combinesTrustLevels combines two trust levels to calculate inherited trust.
// With numeric trust levels (0-100), inherited trust is calculated by
// multiplying the two percentages: (level1 * level2) / 100
// This naturally reduces trust over distance.
func (tc *TrustCalculator) combinesTrustLevels(level1, level2 TrustLevel) TrustLevel {
	// Multiply percentages: (level1% * level2%) = (level1 * level2) / 100
	// Example: 75% trust * 50% trust = 37.5% inherited trust
	combined := (uint16(level1) * uint16(level2)) / 100
	return TrustLevel(combined)
}

// ReplicationFilter helps determine which events should be replicated.
type ReplicationFilter struct {
	trustCalculator *TrustCalculator
	acts            map[string]*TrustAct
}

// NewReplicationFilter creates a new replication filter.
func NewReplicationFilter(trustCalculator *TrustCalculator) *ReplicationFilter {
	return &ReplicationFilter{
		trustCalculator: trustCalculator,
		acts:            make(map[string]*TrustAct),
	}
}

// AddTrustAct adds a trust act to the filter.
func (rf *ReplicationFilter) AddTrustAct(act *TrustAct) {
	rf.acts[act.GetTargetPubkey()] = act
}

// ShouldReplicate determines if an event should be replicated to a target relay.
func (rf *ReplicationFilter) ShouldReplicate(ev *event.E, targetPubkey string) bool {
	act, exists := rf.acts[targetPubkey]
	if !exists || act.IsExpired() {
		return false
	}

	return act.ShouldReplicate(ev.Kind)
}

// GetReplicationTargets returns all target relays that should receive an event.
func (rf *ReplicationFilter) GetReplicationTargets(ev *event.E) []string {
	var targets []string

	for pubkey, act := range rf.acts {
		if !act.IsExpired() && act.ShouldReplicate(ev.Kind) {
			targets = append(targets, pubkey)
		}
	}

	return targets
}

// EventBatcher helps batch events for efficient replication.
type EventBatcher struct {
	maxBatchSize int
	batches      map[string][]*event.E
}

// NewEventBatcher creates a new event batcher.
func NewEventBatcher(maxBatchSize int) *EventBatcher {
	if maxBatchSize <= 0 {
		maxBatchSize = 100 // Default batch size
	}

	return &EventBatcher{
		maxBatchSize: maxBatchSize,
		batches:      make(map[string][]*event.E),
	}
}

// AddEvent adds an event to the batch for a target relay.
func (eb *EventBatcher) AddEvent(targetRelay string, ev *event.E) {
	eb.batches[targetRelay] = append(eb.batches[targetRelay], ev)
}

// GetBatch returns the current batch for a target relay.
func (eb *EventBatcher) GetBatch(targetRelay string) []*event.E {
	return eb.batches[targetRelay]
}

// IsBatchFull returns true if the batch for a target relay is full.
func (eb *EventBatcher) IsBatchFull(targetRelay string) bool {
	return len(eb.batches[targetRelay]) >= eb.maxBatchSize
}

// FlushBatch returns and clears the batch for a target relay.
func (eb *EventBatcher) FlushBatch(targetRelay string) []*event.E {
	batch := eb.batches[targetRelay]
	eb.batches[targetRelay] = nil
	return batch
}

// GetAllBatches returns all current batches.
func (eb *EventBatcher) GetAllBatches() map[string][]*event.E {
	result := make(map[string][]*event.E)
	for relay, batch := range eb.batches {
		if len(batch) > 0 {
			result[relay] = batch
		}
	}
	return result
}

// FlushAllBatches returns and clears all batches.
func (eb *EventBatcher) FlushAllBatches() map[string][]*event.E {
	result := eb.GetAllBatches()
	eb.batches = make(map[string][]*event.E)
	return result
}

// Utility functions

// ParseKindsList parses a comma-separated list of event kinds.
func ParseKindsList(kindsStr string) (kinds []uint16, err error) {
	if kindsStr == "" {
		return nil, nil
	}

	kindStrings := strings.Split(kindsStr, ",")
	for _, kindStr := range kindStrings {
		kindStr = strings.TrimSpace(kindStr)
		if kindStr == "" {
			continue
		}

		var kind uint64
		if kind, err = strconv.ParseUint(kindStr, 10, 16); chk.E(err) {
			return nil, errorf.E("invalid kind: %s", kindStr)
		}

		kinds = append(kinds, uint16(kind))
	}

	return
}

// FormatKindsList formats a list of event kinds as a comma-separated string.
func FormatKindsList(kinds []uint16) string {
	if len(kinds) == 0 {
		return ""
	}

	var kindStrings []string
	for _, kind := range kinds {
		kindStrings = append(kindStrings, strconv.FormatUint(uint64(kind), 10))
	}

	return strings.Join(kindStrings, ",")
}

// GenerateRequestID generates a unique request ID for replication requests.
func GenerateRequestID() (requestID string, err error) {
	// Use timestamp + random nonce for uniqueness
	timestamp := time.Now().Unix()
	var nonce string
	if nonce, err = GenerateNonceHex(8); chk.E(err) {
		return
	}

	requestID = fmt.Sprintf("%d-%s", timestamp, nonce)
	return
}

// CreateSuccessResponse creates a successful replication response.
func CreateSuccessResponse(
	pubkey []byte,
	requestID, sourceRelay string,
	eventResults []*EventResult,
) (response *DirectoryEventReplicationResponse, err error) {
	return NewDirectoryEventReplicationResponse(
		pubkey,
		requestID,
		ReplicationStatusSuccess,
		"",
		sourceRelay,
		eventResults,
	)
}

// CreateErrorResponse creates an error replication response.
func CreateErrorResponse(
	pubkey []byte,
	requestID, sourceRelay, errorMsg string,
) (response *DirectoryEventReplicationResponse, err error) {
	return NewDirectoryEventReplicationResponse(
		pubkey,
		requestID,
		ReplicationStatusError,
		errorMsg,
		sourceRelay,
		nil,
	)
}

// CreateEventResult creates an event result for a replication response.
func CreateEventResult(eventID string, success bool, errorMsg string) *EventResult {
	status := ReplicationStatusSuccess
	if !success {
		status = ReplicationStatusError
	}

	return &EventResult{
		EventID: eventID,
		Status:  status,
		Error:   errorMsg,
	}
}
