package torrent

import (
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"strings"
	"time"
)

const (
	// DefaultPieceSize 默认分片大小 10MB，与天翼云 CAS 分片大小一致
	DefaultPieceSize int64 = 10 * 1024 * 1024

	// CASExtensionKey torrent 根字典中的 CAS 扩展 key
	CASExtensionKey = "x-cas"

	// CASSliceSizeKey CAS 分片大小 key
	CASSliceSizeKey = "slice_size"
	// CASSliceMD5sKey 每片 MD5 列表 key
	CASSliceMD5sKey = "slice_md5s"
	// CASSliceMD5Key 最终 sliceMd5 key
	CASSliceMD5Key = "slice_md5"
	// CASFileMD5Key 整文件 MD5 key
	CASFileMD5Key = "file_md5"
	// CASCloudKey 云盘类型 key
	CASCloudKey = "cloud"
)

// CASInfo 天翼云 CAS 秒传所需信息
type CASInfo struct {
	// FileMD5 整文件 MD5（大写十六进制）
	FileMD5 string
	// SliceMD5 分片 MD5 的摘要（大写十六进制）
	SliceMD5 string
	// SliceMD5s 每个 10MB 分片的 MD5（大写十六进制）
	SliceMD5s []string
	// SliceSize 分片大小（字节）
	SliceSize int64
	// Cloud 云盘类型标识
	Cloud string
}

// TorrentFile 表示 torrent 中的单个文件
type TorrentFile struct {
	// Length 文件大小（字节）
	Length int64
	// Path 文件路径（多文件模式下的相对路径各段）
	Path []string
	// MD5Sum 文件的 MD5（可选，BT 标准字段）
	MD5Sum string
}

// TorrentInfo torrent 的 info 字典
type TorrentInfo struct {
	// PieceLength 分片大小
	PieceLength int64
	// Pieces 所有分片的 SHA-1 哈希拼接（每 20 字节一个）
	Pieces []byte
	// Name 种子名称（单文件模式为文件名，多文件模式为目录名）
	Name string
	// Length 单文件模式下的文件大小
	Length int64
	// Files 多文件模式下的文件列表
	Files []TorrentFile
	// MD5Sum 单文件模式下的文件 MD5（可选）
	MD5Sum string
}

// Torrent 完整的 torrent 文件结构
type Torrent struct {
	// Info info 字典
	Info TorrentInfo
	// InfoHash info 字典的 SHA-1 哈希（20 字节）
	InfoHash []byte
	// Announce tracker URL
	Announce string
	// AnnounceList tracker 列表
	AnnounceList [][]string
	// CreationDate 创建时间
	CreationDate int64
	// Comment 注释
	Comment string
	// CreatedBy 创建者
	CreatedBy string
	// CAS 天翼云 CAS 扩展信息（存储在 info 字典外部，不影响 info_hash）
	CAS *CASInfo
}

// NewTorrent 创建一个新的 torrent 结构
func NewTorrent(name string, fileSize int64, fileMD5 string) *Torrent {
	return &Torrent{
		Info: TorrentInfo{
			PieceLength: DefaultPieceSize,
			Name:        name,
			Length:      fileSize,
			MD5Sum:      fileMD5,
		},
		CreationDate: time.Now().Unix(),
		CreatedBy:    "OpenList",
		Comment:      "Generated by OpenList with CAS extension",
	}
}

// SetPieces 设置 SHA-1 分片哈希
func (t *Torrent) SetPieces(pieces []byte) {
	t.Info.Pieces = pieces
}

// SetCASInfo 设置 CAS 扩展信息
func (t *Torrent) SetCASInfo(cas *CASInfo) {
	t.CAS = cas
}

// Encode 将 torrent 编码为 bencode 格式的字节数组
func (t *Torrent) Encode() ([]byte, error) {
	// 构建 info 字典
	infoDict := make(map[string]interface{})
	infoDict["piece length"] = int64(t.Info.PieceLength)
	infoDict["pieces"] = t.Info.Pieces
	infoDict["name"] = t.Info.Name

	if len(t.Info.Files) > 0 {
		// 多文件模式
		files := make([]interface{}, 0, len(t.Info.Files))
		for _, f := range t.Info.Files {
			fileDict := make(map[string]interface{})
			fileDict["length"] = int64(f.Length)
			path := make([]interface{}, 0, len(f.Path))
			for _, p := range f.Path {
				path = append(path, p)
			}
			fileDict["path"] = path
			if f.MD5Sum != "" {
				fileDict["md5sum"] = f.MD5Sum
			}
			files = append(files, fileDict)
		}
		infoDict["files"] = files
	} else {
		// 单文件模式
		infoDict["length"] = int64(t.Info.Length)
		if t.Info.MD5Sum != "" {
			infoDict["md5sum"] = t.Info.MD5Sum
		}
	}

	// 编码 info 字典并计算 info_hash
	infoBytes, err := BencodeEncode(infoDict)
	if err != nil {
		return nil, fmt.Errorf("encode info dict: %w", err)
	}
	infoHashRaw := sha1.Sum(infoBytes)
	t.InfoHash = infoHashRaw[:]

	// 构建根字典
	rootDict := make(map[string]interface{})
	if t.Announce != "" {
		rootDict["announce"] = t.Announce
	}
	if len(t.AnnounceList) > 0 {
		announceList := make([]interface{}, 0, len(t.AnnounceList))
		for _, tier := range t.AnnounceList {
			tierList := make([]interface{}, 0, len(tier))
			for _, url := range tier {
				tierList = append(tierList, url)
			}
			announceList = append(announceList, tierList)
		}
		rootDict["announce-list"] = announceList
	}
	if t.Comment != "" {
		rootDict["comment"] = t.Comment
	}
	if t.CreatedBy != "" {
		rootDict["created by"] = t.CreatedBy
	}
	if t.CreationDate > 0 {
		rootDict["creation date"] = t.CreationDate
	}

	// info 字典使用原始编码的字节（保证 info_hash 一致）
	rootDict["info"] = infoDict

	// CAS 扩展信息（放在 info 外部，不影响 info_hash）
	if t.CAS != nil {
		casDict := make(map[string]interface{})
		casDict[CASCloudKey] = t.CAS.Cloud
		casDict[CASFileMD5Key] = t.CAS.FileMD5
		casDict[CASSliceMD5Key] = t.CAS.SliceMD5
		casDict[CASSliceSizeKey] = t.CAS.SliceSize

		if len(t.CAS.SliceMD5s) > 0 {
			md5List := make([]interface{}, 0, len(t.CAS.SliceMD5s))
			for _, md5 := range t.CAS.SliceMD5s {
				md5List = append(md5List, md5)
			}
			casDict[CASSliceMD5sKey] = md5List
		}
		rootDict[CASExtensionKey] = casDict
	}

	return BencodeEncode(rootDict)
}

// Decode 从 bencode 字节数组解析 torrent
func Decode(data []byte) (*Torrent, error) {
	val, err := BencodeDecode(data)
	if err != nil {
		return nil, fmt.Errorf("bencode decode: %w", err)
	}

	rootDict, ok := val.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("torrent: root is not a dict")
	}

	t := &Torrent{}

	// 解析 announce
	if v, ok := rootDict["announce"]; ok {
		if b, ok := v.([]byte); ok {
			t.Announce = string(b)
		}
	}

	// 解析 announce-list
	if v, ok := rootDict["announce-list"]; ok {
		if list, ok := v.([]interface{}); ok {
			for _, tier := range list {
				if tierList, ok := tier.([]interface{}); ok {
					var urls []string
					for _, u := range tierList {
						if b, ok := u.([]byte); ok {
							urls = append(urls, string(b))
						}
					}
					if len(urls) > 0 {
						t.AnnounceList = append(t.AnnounceList, urls)
					}
				}
			}
		}
	}

	// 解析 comment
	if v, ok := rootDict["comment"]; ok {
		if b, ok := v.([]byte); ok {
			t.Comment = string(b)
		}
	}

	// 解析 created by
	if v, ok := rootDict["created by"]; ok {
		if b, ok := v.([]byte); ok {
			t.CreatedBy = string(b)
		}
	}

	// 解析 creation date
	if v, ok := rootDict["creation date"]; ok {
		if n, ok := v.(int64); ok {
			t.CreationDate = n
		}
	}

	// 解析 info 字典
	if infoVal, ok := rootDict["info"]; ok {
		infoDict, ok := infoVal.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("torrent: info is not a dict")
		}

		// 计算 info_hash
		infoBytes, err := BencodeEncode(infoDict)
		if err != nil {
			return nil, fmt.Errorf("encode info for hash: %w", err)
		}
		infoHashRaw := sha1.Sum(infoBytes)
		t.InfoHash = infoHashRaw[:]

		// 解析 info 字段
		if v, ok := infoDict["piece length"]; ok {
			if n, ok := v.(int64); ok {
				t.Info.PieceLength = n
			}
		}
		if v, ok := infoDict["pieces"]; ok {
			if b, ok := v.([]byte); ok {
				t.Info.Pieces = b
			}
		}
		if v, ok := infoDict["name"]; ok {
			if b, ok := v.([]byte); ok {
				t.Info.Name = string(b)
			}
		}
		if v, ok := infoDict["length"]; ok {
			if n, ok := v.(int64); ok {
				t.Info.Length = n
			}
		}
		if v, ok := infoDict["md5sum"]; ok {
			if b, ok := v.([]byte); ok {
				t.Info.MD5Sum = string(b)
			}
		}

		// 解析多文件模式
		if v, ok := infoDict["files"]; ok {
			if files, ok := v.([]interface{}); ok {
				for _, f := range files {
					if fileDict, ok := f.(map[string]interface{}); ok {
						tf := TorrentFile{}
						if l, ok := fileDict["length"]; ok {
							if n, ok := l.(int64); ok {
								tf.Length = n
							}
						}
						if p, ok := fileDict["path"]; ok {
							if pathList, ok := p.([]interface{}); ok {
								for _, pp := range pathList {
									if b, ok := pp.([]byte); ok {
										tf.Path = append(tf.Path, string(b))
									}
								}
							}
						}
						if m, ok := fileDict["md5sum"]; ok {
							if b, ok := m.([]byte); ok {
								tf.MD5Sum = string(b)
							}
						}
						t.Info.Files = append(t.Info.Files, tf)
					}
				}
			}
		}
	}

	// 解析 CAS 扩展
	if casVal, ok := rootDict[CASExtensionKey]; ok {
		if casDict, ok := casVal.(map[string]interface{}); ok {
			cas := &CASInfo{}
			if v, ok := casDict[CASCloudKey]; ok {
				if b, ok := v.([]byte); ok {
					cas.Cloud = string(b)
				}
			}
			if v, ok := casDict[CASFileMD5Key]; ok {
				if b, ok := v.([]byte); ok {
					cas.FileMD5 = string(b)
				}
			}
			if v, ok := casDict[CASSliceMD5Key]; ok {
				if b, ok := v.([]byte); ok {
					cas.SliceMD5 = string(b)
				}
			}
			if v, ok := casDict[CASSliceSizeKey]; ok {
				if n, ok := v.(int64); ok {
					cas.SliceSize = n
				}
			}
			if v, ok := casDict[CASSliceMD5sKey]; ok {
				if list, ok := v.([]interface{}); ok {
					for _, item := range list {
						if b, ok := item.([]byte); ok {
							cas.SliceMD5s = append(cas.SliceMD5s, string(b))
						}
					}
				}
			}
			t.CAS = cas
		}
	}

	return t, nil
}

// GetInfoHashHex 获取 info_hash 的十六进制字符串
func (t *Torrent) GetInfoHashHex() string {
	return hex.EncodeToString(t.InfoHash)
}

// GetPieceHashes 获取所有分片的 SHA-1 哈希（每个 20 字节）
func (t *Torrent) GetPieceHashes() [][]byte {
	if len(t.Info.Pieces) == 0 {
		return nil
	}
	count := len(t.Info.Pieces) / 20
	hashes := make([][]byte, count)
	for i := 0; i < count; i++ {
		hashes[i] = t.Info.Pieces[i*20 : (i+1)*20]
	}
	return hashes
}

// GetTotalSize 获取 torrent 中所有文件的总大小
func (t *Torrent) GetTotalSize() int64 {
	if len(t.Info.Files) > 0 {
		var total int64
		for _, f := range t.Info.Files {
			total += f.Length
		}
		return total
	}
	return t.Info.Length
}

// HasCASInfo 检查 torrent 是否包含 CAS 扩展信息
func (t *Torrent) HasCASInfo() bool {
	return t.CAS != nil && t.CAS.FileMD5 != "" && t.CAS.SliceMD5 != ""
}

// BuildCASInfoFromMD5s 从分片 MD5 列表构建 CAS 信息
func BuildCASInfoFromMD5s(fileMD5 string, sliceMD5s []string, sliceSize int64) *CASInfo {
	sliceMD5 := fileMD5
	if len(sliceMD5s) > 1 {
		// 所有分片 MD5 用 \n 拼接后再取 MD5
		joined := strings.Join(sliceMD5s, "\n")
		sliceMD5 = strings.ToUpper(GetMD5Str(joined))
	}
	return &CASInfo{
		FileMD5:   fileMD5,
		SliceMD5:  sliceMD5,
		SliceMD5s: sliceMD5s,
		SliceSize: sliceSize,
		Cloud:     "189",
	}
}

// GetMD5Str 计算字符串的 MD5（大写十六进制）
func GetMD5Str(data string) string {
	h := md5.New()
	h.Write([]byte(data))
	return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
}
