// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package torrent

import (
	"fmt"
	"os"
	"time"

	"yunion.io/x/log"
	"yunion.io/x/pkg/util/httputils"

	identity_apis "yunion.io/x/onecloud/pkg/apis/identity"
	"yunion.io/x/onecloud/pkg/appsrv"
	"yunion.io/x/onecloud/pkg/image/options"
	"yunion.io/x/onecloud/pkg/mcclient/auth"
	"yunion.io/x/onecloud/pkg/util/sysutils"
)

type STorrentProcessState struct {
	process *os.Process
	seeding bool
}

var (
	torrentTable      = make(map[string]*STorrentProcessState)
	seedTaskWorkerMan *appsrv.SWorkerManager
)

const (
	TORRENT_TRACKER_SERVICE = "torrent-tracker"
)

func init() {
	seedTaskWorkerMan = appsrv.NewWorkerManager("seedTaskWorkerManager", 1, 1024, false)
}

func (stat *STorrentProcessState) StopAndWait() error {
	err := stat.process.Kill()
	if err != nil {
		log.Errorf("kill error %s", err)
		return err
	}
	_, err = stat.process.Wait()
	if err != nil {
		log.Errorf("wait error %s", err)
		return err
	}
	return nil
}

func GetTrackers() []string {
	urls, err := auth.GetServiceURLs(TORRENT_TRACKER_SERVICE, options.Options.Region, "", "", httputils.POST)
	if err != nil {
		log.Errorf("fail to get torrent-tracker")
		return nil
	}
	return urls
}

type torrentTask struct {
	torrentpath string
	imageId     string
	format      string
}

func (t *torrentTask) Run() {
	log.Infof("Start seed %s ...", t.torrentpath)
	err := seedTorrent(t.torrentpath, t.imageId, t.format)
	if err == nil {
		time.Sleep(10 * time.Second)
	}
}

func (t *torrentTask) Dump() string {
	return fmt.Sprintf("torrentpath: %s imageId: %s.%s", t.torrentpath, t.imageId, t.format)
}

func SeedTorrent(torrentpath string, imageId, format string) error {
	task := &torrentTask{
		torrentpath: torrentpath,
		imageId:     imageId,
		format:      format,
	}
	seedTaskWorkerMan.Run(task, nil, nil)
	return nil
}

func seedTorrent(torrentpath string, imageId, format string) error {
	url, err := auth.GetServiceURL("image", options.Options.Region, "", identity_apis.EndpointInterfacePublic, httputils.POST)
	if err != nil {
		return err
	}
	args := []string{
		options.Options.TorrentClientPath,
		options.Options.FilesystemStoreDatadir,
		torrentpath,
		"--callback-url",
		fmt.Sprintf("%s/images/%s/update-torrent-status?format=%s", url, imageId, format),
	}
	proc, err := sysutils.Start(false, args...)
	if err != nil {
		return err
	}
	torrentTable[torrentpath] = &STorrentProcessState{
		process: proc,
		seeding: false,
	}
	return nil
}

func SetTorrentSeeding(filepath string, seeding bool) {
	if _, ok := torrentTable[filepath]; ok {
		torrentTable[filepath].seeding = seeding
	}
}

func GetTorrentSeeding(filepath string) bool {
	if t, ok := torrentTable[filepath]; ok {
		return t.seeding
	}
	return false
}

func RemoveTorrent(filepath string) {
	if t, ok := torrentTable[filepath]; ok {
		t.StopAndWait()
		delete(torrentTable, filepath)
	}
}

func StopTorrents() {
	for k := range torrentTable {
		torrentTable[k].StopAndWait()
	}
}
