package find

import (
	"fmt"
	"time"

	"git.mleku.dev/mleku/nostr/encoders/hex"
	"git.mleku.dev/mleku/nostr/encoders/event"
	"git.mleku.dev/mleku/nostr/interfaces/signer/p8k"
)

// VerifyEvent verifies the signature of a Nostr event
func VerifyEvent(ev *event.E) error {
	ok, err := ev.Verify()
	if err != nil {
		return fmt.Errorf("signature verification failed: %w", err)
	}
	if !ok {
		return fmt.Errorf("invalid signature")
	}
	return nil
}

// VerifyTransferAuth verifies a transfer authorization signature
func VerifyTransferAuth(name, newOwner, prevOwner string, timestamp time.Time, sigHex string) (bool, error) {
	// Create the message
	msgHash := CreateTransferAuthMessage(name, newOwner, timestamp)

	// Decode signature
	sig, err := hex.Dec(sigHex)
	if err != nil {
		return false, fmt.Errorf("invalid signature hex: %w", err)
	}

	// Decode pubkey
	pubkey, err := hex.Dec(prevOwner)
	if err != nil {
		return false, fmt.Errorf("invalid pubkey hex: %w", err)
	}

	// Create verifier with public key
	verifier, err := p8k.New()
	if err != nil {
		return false, fmt.Errorf("failed to create verifier: %w", err)
	}

	if err := verifier.InitPub(pubkey); err != nil {
		return false, fmt.Errorf("failed to init pubkey: %w", err)
	}

	// Verify signature
	ok, err := verifier.Verify(msgHash, sig)
	if err != nil {
		return false, fmt.Errorf("verification failed: %w", err)
	}

	return ok, nil
}

// VerifyChallengeProof verifies a certificate challenge proof signature
func VerifyChallengeProof(challenge, name, certPubkey, owner string, validUntil time.Time, sigHex string) (bool, error) {
	// Create the message
	msgHash := CreateChallengeProofMessage(challenge, name, certPubkey, validUntil)

	// Decode signature
	sig, err := hex.Dec(sigHex)
	if err != nil {
		return false, fmt.Errorf("invalid signature hex: %w", err)
	}

	// Decode pubkey
	pubkey, err := hex.Dec(owner)
	if err != nil {
		return false, fmt.Errorf("invalid pubkey hex: %w", err)
	}

	// Create verifier with public key
	verifier, err := p8k.New()
	if err != nil {
		return false, fmt.Errorf("failed to create verifier: %w", err)
	}

	if err := verifier.InitPub(pubkey); err != nil {
		return false, fmt.Errorf("failed to init pubkey: %w", err)
	}

	// Verify signature
	ok, err := verifier.Verify(msgHash, sig)
	if err != nil {
		return false, fmt.Errorf("verification failed: %w", err)
	}

	return ok, nil
}

// VerifyWitnessSignature verifies a witness signature on a certificate
func VerifyWitnessSignature(certPubkey, name string, validFrom, validUntil time.Time,
	challenge, witnessPubkey, sigHex string) (bool, error) {

	// Create the message
	msgHash := CreateWitnessMessage(certPubkey, name, validFrom, validUntil, challenge)

	// Decode signature
	sig, err := hex.Dec(sigHex)
	if err != nil {
		return false, fmt.Errorf("invalid signature hex: %w", err)
	}

	// Decode pubkey
	pubkey, err := hex.Dec(witnessPubkey)
	if err != nil {
		return false, fmt.Errorf("invalid pubkey hex: %w", err)
	}

	// Create verifier with public key
	verifier, err := p8k.New()
	if err != nil {
		return false, fmt.Errorf("failed to create verifier: %w", err)
	}

	if err := verifier.InitPub(pubkey); err != nil {
		return false, fmt.Errorf("failed to init pubkey: %w", err)
	}

	// Verify signature
	ok, err := verifier.Verify(msgHash, sig)
	if err != nil {
		return false, fmt.Errorf("verification failed: %w", err)
	}

	return ok, nil
}

// VerifyNameOwnership checks if a record's owner matches the name state owner
func VerifyNameOwnership(nameState *NameState, record *NameRecord) error {
	recordOwner := hex.Enc(record.Event.Pubkey)
	if recordOwner != nameState.Owner {
		return fmt.Errorf("%w: record owner %s != name owner %s",
			ErrNotOwner, recordOwner, nameState.Owner)
	}
	return nil
}

// IsExpired checks if a time-based expiration has passed
func IsExpired(expiration time.Time) bool {
	return time.Now().After(expiration)
}

// IsInRenewalWindow checks if the current time is within the preferential renewal window
// (final 30 days before expiration)
func IsInRenewalWindow(expiration time.Time) bool {
	now := time.Now()
	renewalWindowStart := expiration.Add(-PreferentialRenewalDays * 24 * time.Hour)
	return now.After(renewalWindowStart) && now.Before(expiration)
}

// CanRegister checks if a name can be registered based on its state and expiration
func CanRegister(nameState *NameState, proposerPubkey string) error {
	// If no name state exists, anyone can register
	if nameState == nil {
		return nil
	}

	// Check if name is expired
	if IsExpired(nameState.Expiration) {
		// Name is expired, anyone can register
		return nil
	}

	// Check if in renewal window
	if IsInRenewalWindow(nameState.Expiration) {
		// Only current owner can register during renewal window
		if proposerPubkey != nameState.Owner {
			return ErrInRenewalWindow
		}
		return nil
	}

	// Name is still owned and not in renewal window
	return fmt.Errorf("name is owned by %s until %s", nameState.Owner, nameState.Expiration)
}

// VerifyProposalExpiration checks if a proposal has expired
func VerifyProposalExpiration(proposal *RegistrationProposal) error {
	if !proposal.Expiration.IsZero() && IsExpired(proposal.Expiration) {
		return fmt.Errorf("proposal expired at %s", proposal.Expiration)
	}
	return nil
}

// VerifyAttestationExpiration checks if an attestation has expired
func VerifyAttestationExpiration(attestation *Attestation) error {
	if !attestation.Expiration.IsZero() && IsExpired(attestation.Expiration) {
		return fmt.Errorf("attestation expired at %s", attestation.Expiration)
	}
	return nil
}

// VerifyTrustGraphExpiration checks if a trust graph has expired
func VerifyTrustGraphExpiration(trustGraph *TrustGraphEvent) error {
	if !trustGraph.Expiration.IsZero() && IsExpired(trustGraph.Expiration) {
		return fmt.Errorf("trust graph expired at %s", trustGraph.Expiration)
	}
	return nil
}

// VerifyNameStateExpiration checks if a name state has expired
func VerifyNameStateExpiration(nameState *NameState) error {
	if !nameState.Expiration.IsZero() && IsExpired(nameState.Expiration) {
		return ErrNameExpired
	}
	return nil
}

// VerifyCertificateValidity checks if a certificate is currently valid
func VerifyCertificateValidity(cert *Certificate) error {
	now := time.Now()

	if now.Before(cert.ValidFrom) {
		return fmt.Errorf("certificate not yet valid (valid from %s)", cert.ValidFrom)
	}

	if now.After(cert.ValidUntil) {
		return fmt.Errorf("certificate expired at %s", cert.ValidUntil)
	}

	return nil
}

// VerifyCertificate performs complete certificate verification
func VerifyCertificate(cert *Certificate, nameState *NameState, trustedWitnesses []string) error {
	// Verify certificate is not expired
	if err := VerifyCertificateValidity(cert); err != nil {
		return err
	}

	// Verify name is not expired
	if err := VerifyNameStateExpiration(nameState); err != nil {
		return err
	}

	// Verify certificate owner matches name owner
	certOwner := hex.Enc(cert.Event.Pubkey)
	if certOwner != nameState.Owner {
		return fmt.Errorf("certificate owner %s != name owner %s", certOwner, nameState.Owner)
	}

	// Verify challenge proof
	ok, err := VerifyChallengeProof(cert.Challenge, cert.Name, cert.CertPubkey,
		nameState.Owner, cert.ValidUntil, cert.ChallengeProof)
	if err != nil {
		return fmt.Errorf("challenge proof verification failed: %w", err)
	}
	if !ok {
		return fmt.Errorf("invalid challenge proof signature")
	}

	// Count trusted witnesses
	trustedCount := 0
	for _, witness := range cert.Witnesses {
		// Check if witness is in trusted list
		isTrusted := false
		for _, trusted := range trustedWitnesses {
			if witness.Pubkey == trusted {
				isTrusted = true
				break
			}
		}

		if !isTrusted {
			continue
		}

		// Verify witness signature
		ok, err := VerifyWitnessSignature(cert.CertPubkey, cert.Name,
			cert.ValidFrom, cert.ValidUntil, cert.Challenge,
			witness.Pubkey, witness.Signature)
		if err != nil {
			return fmt.Errorf("witness %s signature verification failed: %w", witness.Pubkey, err)
		}
		if !ok {
			return fmt.Errorf("invalid witness %s signature", witness.Pubkey)
		}

		trustedCount++
	}

	// Require at least 3 trusted witnesses
	if trustedCount < 3 {
		return fmt.Errorf("insufficient trusted witnesses: %d < 3", trustedCount)
	}

	return nil
}

// VerifySubdomainAuthority checks if the proposer owns the parent domain
func VerifySubdomainAuthority(name string, proposerPubkey string, parentNameState *NameState) error {
	parent := GetParentDomain(name)

	// TLDs have no parent
	if parent == "" {
		return nil
	}

	// Parent must exist
	if parentNameState == nil {
		return fmt.Errorf("parent domain %s does not exist", parent)
	}

	// Proposer must own parent
	if proposerPubkey != parentNameState.Owner {
		return fmt.Errorf("proposer %s does not own parent domain %s (owner: %s)",
			proposerPubkey, parent, parentNameState.Owner)
	}

	return nil
}
