package tracker

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strconv"
)

// BencodeEncoder provides additional bencode utilities beyond the anacrolix library
type BencodeEncoder struct{}

// NewBencodeEncoder creates a new bencode encoder
func NewBencodeEncoder() *BencodeEncoder {
	return &BencodeEncoder{}
}

// EncodeResponse encodes a tracker response with proper bencode formatting
func (e *BencodeEncoder) EncodeResponse(resp *AnnounceResponse) ([]byte, error) {
	var buf bytes.Buffer

	// Start dictionary
	buf.WriteString("d")

	// Add fields in alphabetical order (bencode requirement)
	if resp.Complete >= 0 {
		buf.WriteString("8:complete")
		buf.WriteString(e.encodeInt(resp.Complete))
	}

	if resp.FailureReason != "" {
		buf.WriteString("14:failure reason")
		buf.WriteString(e.encodeString(resp.FailureReason))
	}

	if resp.Incomplete >= 0 {
		buf.WriteString("10:incomplete")
		buf.WriteString(e.encodeInt(resp.Incomplete))
	}

	if resp.Interval > 0 {
		buf.WriteString("8:interval")
		buf.WriteString(e.encodeInt(resp.Interval))
	}

	if resp.MinInterval > 0 {
		buf.WriteString("12:min interval")
		buf.WriteString(e.encodeInt(resp.MinInterval))
	}

	// Encode peers
	if resp.Peers != nil {
		buf.WriteString("5:peers")
		if peerBytes, ok := resp.Peers.([]byte); ok {
			// Compact format
			buf.WriteString(e.encodeBytes(peerBytes))
		} else if dictPeers, ok := resp.Peers.([]DictPeer); ok {
			// Dictionary format
			buf.WriteString("l") // Start list
			for _, peer := range dictPeers {
				buf.WriteString("d") // Start peer dict
				buf.WriteString("2:ip")
				buf.WriteString(e.encodeString(peer.IP))
				buf.WriteString("7:peer id")
				buf.WriteString(e.encodeString(peer.PeerID))
				buf.WriteString("4:port")
				buf.WriteString(e.encodeInt(peer.Port))
				buf.WriteString("e") // End peer dict
			}
			buf.WriteString("e") // End list
		}
	}

	if resp.TrackerID != "" {
		buf.WriteString("10:tracker id")
		buf.WriteString(e.encodeString(resp.TrackerID))
	}

	if resp.WarningMessage != "" {
		buf.WriteString("15:warning message")
		buf.WriteString(e.encodeString(resp.WarningMessage))
	}

	// End dictionary
	buf.WriteString("e")

	return buf.Bytes(), nil
}

// encodeString encodes a string in bencode format
func (e *BencodeEncoder) encodeString(s string) string {
	return fmt.Sprintf("%d:%s", len(s), s)
}

// encodeBytes encodes bytes in bencode format
func (e *BencodeEncoder) encodeBytes(b []byte) string {
	return fmt.Sprintf("%d:", len(b)) + string(b)
}

// encodeInt encodes an integer in bencode format
func (e *BencodeEncoder) encodeInt(i int) string {
	return fmt.Sprintf("i%de", i)
}

// ParseAnnounceQuery parses URL-encoded announce parameters with proper bencode handling
func ParseAnnounceQuery(query map[string][]string) (map[string]interface{}, error) {
	result := make(map[string]interface{})

	for key, values := range query {
		if len(values) == 0 {
			continue
		}

		value := values[0]
		switch key {
		case "info_hash", "peer_id":
			// These are binary data that may be URL-encoded
			result[key] = value
		case "port", "uploaded", "downloaded", "left", "numwant":
			if i, err := strconv.ParseInt(value, 10, 64); err == nil {
				result[key] = i
			}
		case "compact":
			result[key] = value == "1"
		default:
			result[key] = value
		}
	}

	return result, nil
}

// BencodeDecoder provides bencode decoding utilities
type BencodeDecoder struct {
	reader *bufio.Reader
}

// NewBencodeDecoder creates a new bencode decoder
func NewBencodeDecoder(r io.Reader) *BencodeDecoder {
	return &BencodeDecoder{
		reader: bufio.NewReader(r),
	}
}

// DecodeDictionary decodes a bencode dictionary
func (d *BencodeDecoder) DecodeDictionary() (map[string]interface{}, error) {
	// Read 'd' marker
	b, err := d.reader.ReadByte()
	if err != nil {
		return nil, err
	}
	if b != 'd' {
		return nil, fmt.Errorf("expected dictionary marker 'd', got %c", b)
	}

	dict := make(map[string]interface{})

	for {
		// Check for end marker
		b, err := d.reader.ReadByte()
		if err != nil {
			return nil, err
		}
		if b == 'e' {
			break
		}
		
		// Put byte back
		d.reader.UnreadByte()

		// Read key (always a string)
		key, err := d.decodeString()
		if err != nil {
			return nil, fmt.Errorf("error reading dictionary key: %w", err)
		}

		// Read value
		value, err := d.decodeValue()
		if err != nil {
			return nil, fmt.Errorf("error reading dictionary value for key %s: %w", key, err)
		}

		dict[key] = value
	}

	return dict, nil
}

// decodeValue decodes any bencode value
func (d *BencodeDecoder) decodeValue() (interface{}, error) {
	b, err := d.reader.ReadByte()
	if err != nil {
		return nil, err
	}

	switch {
	case b >= '0' && b <= '9':
		// String - put byte back and decode
		d.reader.UnreadByte()
		return d.decodeString()
	case b == 'i':
		// Integer
		return d.decodeInteger()
	case b == 'l':
		// List
		return d.decodeList()
	case b == 'd':
		// Dictionary - put byte back and decode
		d.reader.UnreadByte()
		return d.DecodeDictionary()
	default:
		return nil, fmt.Errorf("unexpected bencode marker: %c", b)
	}
}

// decodeString decodes a bencode string
func (d *BencodeDecoder) decodeString() (string, error) {
	// Read length
	var lengthBytes []byte
	for {
		b, err := d.reader.ReadByte()
		if err != nil {
			return "", err
		}
		if b == ':' {
			break
		}
		lengthBytes = append(lengthBytes, b)
	}

	length, err := strconv.Atoi(string(lengthBytes))
	if err != nil {
		return "", fmt.Errorf("invalid string length: %s", string(lengthBytes))
	}

	// Read string data
	data := make([]byte, length)
	_, err = io.ReadFull(d.reader, data)
	if err != nil {
		return "", err
	}

	return string(data), nil
}

// decodeInteger decodes a bencode integer
func (d *BencodeDecoder) decodeInteger() (int64, error) {
	var intBytes []byte
	for {
		b, err := d.reader.ReadByte()
		if err != nil {
			return 0, err
		}
		if b == 'e' {
			break
		}
		intBytes = append(intBytes, b)
	}

	return strconv.ParseInt(string(intBytes), 10, 64)
}

// decodeList decodes a bencode list
func (d *BencodeDecoder) decodeList() ([]interface{}, error) {
	var list []interface{}

	for {
		// Check for end marker
		b, err := d.reader.ReadByte()
		if err != nil {
			return nil, err
		}
		if b == 'e' {
			break
		}
		
		// Put byte back
		d.reader.UnreadByte()

		// Read value
		value, err := d.decodeValue()
		if err != nil {
			return nil, err
		}

		list = append(list, value)
	}

	return list, nil
}