package downloader

import (
	"fmt"
	"io"

	lru "github.com/hashicorp/golang-lru/v2"
	"gitlink.org.cn/cloudream/common/pkgs/iterator"
	"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
	"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader/strategy"
	"gitlink.org.cn/cloudream/jcs-pub/client/types"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/connectivity"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
)

const (
	DefaultMaxStripCacheCount = 128
)

type DownloadIterator = iterator.Iterator[*Downloading]

type DownloadReqeust struct {
	ObjectID types.ObjectID
	Offset   int64
	Length   int64
}

type downloadReqeust2 struct {
	Detail *types.ObjectDetail
	Raw    DownloadReqeust
}

type Downloading struct {
	Object  *types.Object
	File    io.ReadCloser // 文件流，如果文件不存在，那么为nil
	Request DownloadReqeust
}

type Downloader struct {
	strips   *StripCache
	cfg      Config
	conn     *connectivity.Collector
	stgPool  *pool.Pool
	selector *strategy.Selector
	db       *db.DB
}

func NewDownloader(cfg Config, conn *connectivity.Collector, stgPool *pool.Pool, sel *strategy.Selector, db *db.DB) *Downloader {
	if cfg.MaxStripCacheCount == 0 {
		cfg.MaxStripCacheCount = DefaultMaxStripCacheCount
	}

	ch, _ := lru.New[ECStripKey, ObjectECStrip](cfg.MaxStripCacheCount)
	return &Downloader{
		strips:   ch,
		cfg:      cfg,
		conn:     conn,
		stgPool:  stgPool,
		selector: sel,
		db:       db,
	}
}

func (d *Downloader) DownloadObjects(reqs []DownloadReqeust) DownloadIterator {
	objIDs := make([]types.ObjectID, len(reqs))
	for i, req := range reqs {
		objIDs[i] = req.ObjectID
	}

	if len(objIDs) == 0 {
		return iterator.Empty[*Downloading]()
	}

	objDetails, err := db.DoTx11(d.db, d.db.Object().BatchGetDetails, objIDs)
	if err != nil {
		return iterator.FuseError[*Downloading](fmt.Errorf("request to db: %w", err))
	}

	detailsMap := make(map[types.ObjectID]*types.ObjectDetail)
	for _, detail := range objDetails {
		d := detail
		detailsMap[detail.Object.ObjectID] = &d
	}

	req2s := make([]downloadReqeust2, len(reqs))
	for i, req := range reqs {
		req2s[i] = downloadReqeust2{
			Detail: detailsMap[req.ObjectID],
			Raw:    req,
		}
	}

	return NewDownloadObjectIterator(d, req2s)
}

func (d *Downloader) DownloadObjectByDetail(detail types.ObjectDetail, off int64, length int64) (*Downloading, error) {
	req2s := []downloadReqeust2{{
		Detail: &detail,
		Raw: DownloadReqeust{
			ObjectID: detail.Object.ObjectID,
			Offset:   off,
			Length:   length,
		},
	}}

	iter := NewDownloadObjectIterator(d, req2s)
	return iter.MoveNext()
}

func (d *Downloader) DownloadPackage(pkgID types.PackageID) DownloadIterator {
	details, err := db.DoTx11(d.db, d.db.Object().GetPackageObjectDetails, pkgID)
	if err != nil {
		return iterator.FuseError[*Downloading](fmt.Errorf("get package object details: %w", err))
	}

	req2s := make([]downloadReqeust2, len(details))
	for i, objDetail := range details {
		dt := objDetail
		req2s[i] = downloadReqeust2{
			Detail: &dt,
			Raw: DownloadReqeust{
				ObjectID: objDetail.Object.ObjectID,
				Offset:   0,
				Length:   objDetail.Object.Size,
			},
		}
	}

	return NewDownloadObjectIterator(d, req2s)
}

type ObjectECStrip struct {
	Data           []byte
	ObjectFileHash types.FileHash // 添加这条缓存时，Object的FileHash
}

type ECStripKey struct {
	ObjectID   types.ObjectID
	StripIndex int64
}

type StripCache = lru.Cache[ECStripKey, ObjectECStrip]
