package torrent

import (
	"crypto/sha1"
	"fmt"
	"net/url"

	"github.com/anacrolix/torrent/bencode"
	"github.com/anacrolix/torrent/metainfo"
)

type TorrentInfo struct {
	InfoHash    string
	TorrentData []byte
	Magnet      string
}

type FileInfo struct {
	Name      string
	Size      int64
	Pieces    []PieceInfo
	WebSeedURL string
}

type PieceInfo struct {
	Index   int
	Hash    [20]byte // SHA-1 hash for BitTorrent compatibility
	SHA256  string   // SHA-256 hash for Blossom
	Length  int
}

func CreateTorrent(fileInfo FileInfo, trackers []string, gatewayURL string, dhtNodes [][]interface{}) (*TorrentInfo, error) {
	// Calculate piece length based on file size (following BUD-10 spec)
	pieceLength := calculatePieceLength(fileInfo.Size)
	
	// Create pieces buffer - concatenated SHA-1 hashes
	var pieces []byte
	for _, piece := range fileInfo.Pieces {
		pieces = append(pieces, piece.Hash[:]...)
	}

	// Create metainfo
	info := metainfo.Info{
		Name:        fileInfo.Name,
		Length:      fileInfo.Size,
		PieceLength: pieceLength,
		Pieces:      pieces,
	}

	// Build announce list with gateway tracker first, then fallbacks
	var announceList metainfo.AnnounceList
	
	// Primary: Gateway's built-in tracker
	if gatewayURL != "" {
		gatewayTracker := fmt.Sprintf("%s/announce", gatewayURL)
		announceList = append(announceList, []string{gatewayTracker})
	}
	
	// Fallbacks: External trackers
	for _, tracker := range trackers {
		announceList = append(announceList, []string{tracker})
	}

	// Primary announce URL (gateway tracker if available, otherwise first external)
	primaryAnnounce := ""
	if len(announceList) > 0 && len(announceList[0]) > 0 {
		primaryAnnounce = announceList[0][0]
	} else if len(trackers) > 0 {
		primaryAnnounce = trackers[0]
	}

	// Convert DHT nodes to metainfo.Node format
	var nodes []metainfo.Node
	for _, nodeArray := range dhtNodes {
		if len(nodeArray) >= 2 {
			// Node format is "host:port" string
			node := metainfo.Node(fmt.Sprintf("%v:%v", nodeArray[0], nodeArray[1]))
			nodes = append(nodes, node)
		}
	}

	mi := metainfo.MetaInfo{
		InfoBytes:    bencode.MustMarshal(info),
		Announce:     primaryAnnounce,
		AnnounceList: announceList,
		Nodes:        nodes, // DHT bootstrap nodes (BEP-5)
	}

	// Add WebSeed support (BEP-19)
	if fileInfo.WebSeedURL != "" {
		mi.UrlList = []string{fileInfo.WebSeedURL}
	}

	// Calculate info hash
	infoHash := mi.HashInfoBytes()

	// Generate torrent data
	torrentData, err := bencode.Marshal(mi)
	if err != nil {
		return nil, fmt.Errorf("error marshaling torrent: %w", err)
	}

	// Generate magnet link with all trackers
	allTrackers := []string{}
	for _, tier := range announceList {
		allTrackers = append(allTrackers, tier...)
	}
	magnet := generateMagnetLink(infoHash, fileInfo.Name, allTrackers, fileInfo.WebSeedURL)

	return &TorrentInfo{
		InfoHash:    fmt.Sprintf("%x", infoHash),
		TorrentData: torrentData,
		Magnet:      magnet,
	}, nil
}

func calculatePieceLength(fileSize int64) int64 {
	// Following BUD-10 piece size strategy
	const (
		KB = 1024
		MB = KB * 1024
		GB = MB * 1024
	)

	switch {
	case fileSize < 50*MB:
		return 256 * KB
	case fileSize < 500*MB:
		return 512 * KB
	case fileSize < 2*GB:
		return 1 * MB
	default:
		return 2 * MB
	}
}

func generateMagnetLink(infoHash [20]byte, name string, trackers []string, webSeedURL string) string {
	params := url.Values{}
	params.Set("xt", fmt.Sprintf("urn:btih:%x", infoHash))
	params.Set("dn", name)

	for _, tracker := range trackers {
		params.Add("tr", tracker)
	}

	if webSeedURL != "" {
		params.Set("ws", webSeedURL)
	}

	return "magnet:?" + params.Encode()
}

// ConvertSHA256ToSHA1 converts SHA-256 data to SHA-1 for BitTorrent compatibility
// This is used when we have chunk data and need both hashes
func ConvertSHA256ToSHA1(data []byte) [20]byte {
	hash := sha1.Sum(data)
	return hash
}

// CreatePieceInfo creates piece info from chunk data
func CreatePieceInfo(index int, data []byte, sha256Hash string) PieceInfo {
	return PieceInfo{
		Index:   index,
		Hash:    ConvertSHA256ToSHA1(data),
		SHA256:  sha256Hash,
		Length:  len(data),
	}
}