package tracker

import (
	"log"
	"sync"
	"time"
)

// PeerManager handles peer lifecycle and cleanup operations
type PeerManager struct {
	tracker *Tracker
	mutex   sync.RWMutex
}

// NewPeerManager creates a new peer manager
func NewPeerManager(tracker *Tracker) *PeerManager {
	pm := &PeerManager{
		tracker: tracker,
	}

	// Start background cleanup routine
	go pm.startCleanupRoutine()

	return pm
}

// AddPeer adds or updates a peer for a specific torrent
func (pm *PeerManager) AddPeer(infoHash string, peer *PeerInfo) {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	// Initialize torrent peer map if not exists
	if pm.tracker.peers[infoHash] == nil {
		pm.tracker.peers[infoHash] = make(map[string]*PeerInfo)
	}

	// Update last seen time
	peer.LastSeen = time.Now()

	// Store peer
	pm.tracker.peers[infoHash][peer.PeerID] = peer

	log.Printf("Added/updated peer %s for torrent %s (left: %d)", 
		peer.PeerID[:8], infoHash[:8], peer.Left)
}

// RemovePeer removes a peer from a specific torrent
func (pm *PeerManager) RemovePeer(infoHash, peerID string) {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	if torrentPeers, exists := pm.tracker.peers[infoHash]; exists {
		if _, peerExists := torrentPeers[peerID]; peerExists {
			delete(torrentPeers, peerID)
			log.Printf("Removed peer %s from torrent %s", peerID[:8], infoHash[:8])

			// Remove empty torrent entries
			if len(torrentPeers) == 0 {
				delete(pm.tracker.peers, infoHash)
				log.Printf("Removed empty torrent %s", infoHash[:8])
			}
		}
	}
}

// GetPeers returns all peers for a specific torrent
func (pm *PeerManager) GetPeers(infoHash string) map[string]*PeerInfo {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()

	if torrentPeers, exists := pm.tracker.peers[infoHash]; exists {
		// Create a copy to avoid concurrent access issues
		peersCopy := make(map[string]*PeerInfo)
		for id, peer := range torrentPeers {
			peersCopy[id] = &PeerInfo{
				PeerID:     peer.PeerID,
				IP:         peer.IP,
				Port:       peer.Port,
				Uploaded:   peer.Uploaded,
				Downloaded: peer.Downloaded,
				Left:       peer.Left,
				LastSeen:   peer.LastSeen,
				Event:      peer.Event,
				Key:        peer.Key,
				Compact:    peer.Compact,
			}
		}
		return peersCopy
	}

	return make(map[string]*PeerInfo)
}

// GetAllTorrents returns info hashes of all tracked torrents
func (pm *PeerManager) GetAllTorrents() []string {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()

	var torrents []string
	for infoHash := range pm.tracker.peers {
		torrents = append(torrents, infoHash)
	}

	return torrents
}

// UpdatePeerStats updates upload/download statistics for a peer
func (pm *PeerManager) UpdatePeerStats(infoHash, peerID string, uploaded, downloaded, left int64) {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	if torrentPeers, exists := pm.tracker.peers[infoHash]; exists {
		if peer, peerExists := torrentPeers[peerID]; peerExists {
			peer.Uploaded = uploaded
			peer.Downloaded = downloaded
			peer.Left = left
			peer.LastSeen = time.Now()
		}
	}
}

// MarkPeerCompleted marks a peer as having completed the download
func (pm *PeerManager) MarkPeerCompleted(infoHash, peerID string) {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	if torrentPeers, exists := pm.tracker.peers[infoHash]; exists {
		if peer, peerExists := torrentPeers[peerID]; peerExists {
			peer.Left = 0
			peer.Event = "completed"
			peer.LastSeen = time.Now()
			log.Printf("Peer %s completed torrent %s", peerID[:8], infoHash[:8])
		}
	}
}

// startCleanupRoutine starts the background cleanup process
func (pm *PeerManager) startCleanupRoutine() {
	if pm.tracker.config.CleanupInterval <= 0 {
		log.Printf("Cleanup routine disabled (interval <= 0)")
		return
	}

	ticker := time.NewTicker(pm.tracker.config.CleanupInterval)
	defer ticker.Stop()

	log.Printf("Starting peer cleanup routine (interval: %v, timeout: %v)", 
		pm.tracker.config.CleanupInterval, pm.tracker.config.PeerTimeout)

	for range ticker.C {
		pm.cleanupExpiredPeers()
	}
}

// cleanupExpiredPeers removes peers that haven't announced recently
func (pm *PeerManager) cleanupExpiredPeers() {
	pm.mutex.Lock()
	defer pm.mutex.Unlock()

	if pm.tracker.config.PeerTimeout <= 0 {
		return
	}

	now := time.Now()
	expiry := now.Add(-pm.tracker.config.PeerTimeout)
	removedPeers := 0
	removedTorrents := 0

	for infoHash, torrentPeers := range pm.tracker.peers {
		initialPeerCount := len(torrentPeers)

		// Remove expired peers
		for peerID, peer := range torrentPeers {
			if peer.LastSeen.Before(expiry) {
				delete(torrentPeers, peerID)
				removedPeers++
			}
		}

		// Remove empty torrents
		if len(torrentPeers) == 0 && initialPeerCount > 0 {
			delete(pm.tracker.peers, infoHash)
			removedTorrents++
		}
	}

	if removedPeers > 0 || removedTorrents > 0 {
		log.Printf("Cleanup completed: removed %d expired peers and %d empty torrents", 
			removedPeers, removedTorrents)
	}
}

// GetTorrentStats returns statistics for a specific torrent
func (pm *PeerManager) GetTorrentStats(infoHash string) map[string]interface{} {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()

	stats := map[string]interface{}{
		"info_hash":  infoHash,
		"seeders":    0,
		"leechers":   0,
		"total":      0,
		"last_activity": "",
	}

	if torrentPeers, exists := pm.tracker.peers[infoHash]; exists {
		var lastActivity time.Time
		
		for _, peer := range torrentPeers {
			if peer.Left == 0 {
				stats["seeders"] = stats["seeders"].(int) + 1
			} else {
				stats["leechers"] = stats["leechers"].(int) + 1
			}
			
			if peer.LastSeen.After(lastActivity) {
				lastActivity = peer.LastSeen
			}
		}

		stats["total"] = len(torrentPeers)
		if !lastActivity.IsZero() {
			stats["last_activity"] = lastActivity.Format(time.RFC3339)
		}
	}

	return stats
}

// GetAllStats returns comprehensive tracker statistics
func (pm *PeerManager) GetAllStats() map[string]interface{} {
	pm.mutex.RLock()
	defer pm.mutex.RUnlock()

	totalTorrents := len(pm.tracker.peers)
	totalPeers := 0
	totalSeeders := 0
	totalLeechers := 0
	var oldestPeer, newestPeer time.Time

	for _, torrentPeers := range pm.tracker.peers {
		totalPeers += len(torrentPeers)
		
		for _, peer := range torrentPeers {
			if peer.Left == 0 {
				totalSeeders++
			} else {
				totalLeechers++
			}

			// Track oldest and newest peer activity
			if oldestPeer.IsZero() || peer.LastSeen.Before(oldestPeer) {
				oldestPeer = peer.LastSeen
			}
			if peer.LastSeen.After(newestPeer) {
				newestPeer = peer.LastSeen
			}
		}
	}

	stats := map[string]interface{}{
		"torrents":      totalTorrents,
		"total_peers":   totalPeers,
		"total_seeders": totalSeeders,
		"total_leechers": totalLeechers,
		"uptime":        time.Since(pm.tracker.startTime).String(),
	}

	if !oldestPeer.IsZero() {
		stats["oldest_peer"] = oldestPeer.Format(time.RFC3339)
	}
	if !newestPeer.IsZero() {
		stats["newest_peer"] = newestPeer.Format(time.RFC3339)
	}

	return stats
}

// ForceCleanup manually triggers peer cleanup
func (pm *PeerManager) ForceCleanup() map[string]interface{} {
	log.Printf("Manual cleanup triggered")
	
	before := pm.GetAllStats()
	pm.cleanupExpiredPeers()
	after := pm.GetAllStats()

	return map[string]interface{}{
		"before": before,
		"after":  after,
	}
}