// Package addressbook manages contacts and their public keys
package addressbook

import (
	"crypto/ed25519"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"sync"
	"time"
)

// Contact represents a contact in the address book
type Contact struct {
	Name         string            `json:"name"`
	PublicKey    string            `json:"public_key"`    // hex-encoded Ed25519 public key
	OnionAddress string            `json:"onion_address"` // Tor hidden service address
	Fingerprint  string            `json:"fingerprint"`   // Short fingerprint for verification
	AddedAt      time.Time         `json:"added_at"`
	LastSeen     time.Time         `json:"last_seen"`
	Verified     bool              `json:"verified"` // Manual verification flag
	Notes        string            `json:"notes"`
	Metadata     map[string]string `json:"metadata,omitempty"`
}

// AddressBook manages a collection of contacts
type AddressBook struct {
	contacts map[string]*Contact // key = public key hex
	mu       sync.RWMutex
	path     string
}

// NewAddressBook creates a new empty address book
func NewAddressBook() *AddressBook {
	return &AddressBook{
		contacts: make(map[string]*Contact),
	}
}

// LoadAddressBook loads an address book from disk
func LoadAddressBook(path string) (*AddressBook, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("failed to read address book: %w", err)
	}

	var contacts []*Contact
	if err := json.Unmarshal(data, &contacts); err != nil {
		return nil, fmt.Errorf("failed to parse address book: %w", err)
	}

	ab := &AddressBook{
		contacts: make(map[string]*Contact),
		path:     path,
	}

	for _, contact := range contacts {
		ab.contacts[contact.PublicKey] = contact
	}

	return ab, nil
}

// Save saves the address book to disk
func (ab *AddressBook) Save(path string) error {
	ab.mu.RLock()
	defer ab.mu.RUnlock()

	// Convert map to slice
	contacts := make([]*Contact, 0, len(ab.contacts))
	for _, contact := range ab.contacts {
		contacts = append(contacts, contact)
	}

	// Marshal to JSON
	data, err := json.MarshalIndent(contacts, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal address book: %w", err)
	}

	// Write to file
	if err := os.WriteFile(path, data, 0600); err != nil {
		return fmt.Errorf("failed to write address book: %w", err)
	}

	ab.path = path
	return nil
}

// AddContact adds a new contact to the address book
func (ab *AddressBook) AddContact(contact *Contact) error {
	ab.mu.Lock()
	defer ab.mu.Unlock()

	// Validate public key
	pubKeyBytes, err := hex.DecodeString(contact.PublicKey)
	if err != nil {
		return fmt.Errorf("invalid public key hex: %w", err)
	}
	if len(pubKeyBytes) != ed25519.PublicKeySize {
		return fmt.Errorf("invalid public key size: expected %d, got %d", ed25519.PublicKeySize, len(pubKeyBytes))
	}

	// Generate fingerprint (first 8 bytes of public key)
	contact.Fingerprint = hex.EncodeToString(pubKeyBytes[:8])
	contact.AddedAt = time.Now()
	contact.LastSeen = time.Now()

	ab.contacts[contact.PublicKey] = contact
	return nil
}

// GetContact retrieves a contact by public key
func (ab *AddressBook) GetContact(publicKey string) (*Contact, bool) {
	ab.mu.RLock()
	defer ab.mu.RUnlock()

	contact, exists := ab.contacts[publicKey]
	return contact, exists
}

// GetContactByName retrieves a contact by name (first match)
func (ab *AddressBook) GetContactByName(name string) (*Contact, bool) {
	ab.mu.RLock()
	defer ab.mu.RUnlock()

	for _, contact := range ab.contacts {
		if contact.Name == name {
			return contact, true
		}
	}
	return nil, false
}

// RemoveContact removes a contact from the address book
func (ab *AddressBook) RemoveContact(publicKey string) bool {
	ab.mu.Lock()
	defer ab.mu.Unlock()

	if _, exists := ab.contacts[publicKey]; exists {
		delete(ab.contacts, publicKey)
		return true
	}
	return false
}

// UpdateLastSeen updates the last seen timestamp for a contact
func (ab *AddressBook) UpdateLastSeen(publicKey string) {
	ab.mu.Lock()
	defer ab.mu.Unlock()

	if contact, exists := ab.contacts[publicKey]; exists {
		contact.LastSeen = time.Now()
	}
}

// MarkVerified marks a contact as manually verified
func (ab *AddressBook) MarkVerified(publicKey string, verified bool) error {
	ab.mu.Lock()
	defer ab.mu.Unlock()

	contact, exists := ab.contacts[publicKey]
	if !exists {
		return fmt.Errorf("contact not found")
	}

	contact.Verified = verified
	return nil
}

// ListContacts returns all contacts
func (ab *AddressBook) ListContacts() []*Contact {
	ab.mu.RLock()
	defer ab.mu.RUnlock()

	contacts := make([]*Contact, 0, len(ab.contacts))
	for _, contact := range ab.contacts {
		contacts = append(contacts, contact)
	}
	return contacts
}

// Count returns the number of contacts
func (ab *AddressBook) Count() int {
	ab.mu.RLock()
	defer ab.mu.RUnlock()
	return len(ab.contacts)
}

// Clear removes all contacts
func (ab *AddressBook) Clear() {
	ab.mu.Lock()
	defer ab.mu.Unlock()
	ab.contacts = make(map[string]*Contact)
}

// VerifyFingerprint checks if a fingerprint matches a public key
func VerifyFingerprint(publicKeyHex, fingerprint string) bool {
	pubKeyBytes, err := hex.DecodeString(publicKeyHex)
	if err != nil {
		return false
	}
	if len(pubKeyBytes) < 8 {
		return false
	}

	expectedFingerprint := hex.EncodeToString(pubKeyBytes[:8])
	return expectedFingerprint == fingerprint
}

// String returns a string representation
func (ab *AddressBook) String() string {
	ab.mu.RLock()
	defer ab.mu.RUnlock()
	return fmt.Sprintf("AddressBook(contacts=%d)", len(ab.contacts))
}
