package ether

import (
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/pingcap/wormhole/pkg/whpb"
)

type workerInfo struct {
	*whpb.Worker
	contracts []uint64
}

func newWorkerInfo(worker *whpb.Worker) *workerInfo {
	return &workerInfo{
		Worker:    worker,
		contracts: make([]uint64, 0, 4),
	}
}

func (w *workerInfo) getWorker() *whpb.Worker {
	return w.Worker
}

func (w *workerInfo) isOnline() bool {
	return (time.Now().Unix() - w.Worker.LastTime) < MaxOfflineThreshold
}

func (w *workerInfo) terminateContract(jobID uint64) {
	index := -1
	for i := range w.contracts {
		if jobID == w.contracts[i] {
			index = i
			break
		}
	}

	if index == -1 {
		return
	}

	var newContracts []uint64
	newContracts = append(newContracts, w.contracts[:index]...)
	newContracts = append(newContracts, w.contracts[index+1:]...)
	w.contracts = newContracts
}

func (w *workerInfo) signContract(jobID uint64) {
	for _, id := range w.contracts {
		if id == jobID {
			return
		}
	}
	w.contracts = append(w.contracts, jobID)
}

func (w *workerInfo) existContract(jobID uint64) bool {
	for _, id := range w.contracts {
		if id == jobID {
			return true
		}
	}

	return false
}

func (w *workerInfo) clone() *workerInfo {
	contracts := make([]uint64, len(w.contracts))
	for i, workerID := range w.contracts {
		contracts[i] = workerID
	}

	return &workerInfo{
		Worker:    proto.Clone(w.Worker).(*whpb.Worker),
		contracts: contracts,
	}
}

func (w *workerInfo) GetStatusScore() int {
	return MaxScore - len(w.contracts)
}

func (w *workerInfo) GetContracts() []uint64 {
	contracts := make([]uint64, len(w.contracts))
	index := 0
	for _, id := range w.contracts {
		contracts[index] = id
		index++
	}
	return contracts
}

// workersInfo contains all worker information.
type workersInfo struct {
	workers map[uint64]*workerInfo
}

func newWorkersInfo() *workersInfo {
	return &workersInfo{
		workers: make(map[uint64]*workerInfo),
	}
}

func (w *workersInfo) getWorker(id uint64) *workerInfo {
	worker, ok := w.workers[id]
	if !ok {
		return nil
	}

	return worker.clone()
}

func (w *workersInfo) setWorker(worker *workerInfo) {
	w.workers[worker.Id] = worker
}

func (w *workersInfo) terminateContract(workerID, jobID uint64) {
	worker, ok := w.workers[workerID]
	if !ok {
		return
	}
	worker.terminateContract(jobID)
}

func (w *workersInfo) signContract(workerID, jobID uint64) {
	worker, ok := w.workers[workerID]
	if !ok {
		return
	}
	worker.signContract(jobID)
}

func (w *workersInfo) existContract(workerID uint64, jobID uint64) bool {
	worker, ok := w.workers[workerID]
	if !ok {
		return false
	}

	return worker.existContract(jobID)
}

func (w *workersInfo) count() int {
	return len(w.workers)
}

func (w *workersInfo) all() []*workerInfo {
	workers := make([]*workerInfo, len(w.workers))
	index := 0
	for _, worker := range w.workers {
		workers[index] = worker.clone()
		index++
	}
	return workers
}
