package resource_limit

import (
	"errors"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/pingcap/log"
	"go.uber.org/zap"
)

type DeleteLeakingEBSJob struct {
	filterTags map[string]string
	// tagKubernetesCluster string
	ec2API ec2iface.EC2API
}

func NewDeleteLeakingEBSJob(region string, tags map[string]string) *DeleteLeakingEBSJob {
	mySession := session.Must(session.NewSession())
	ec2API := ec2.New(mySession, aws.NewConfig().WithRegion(region))
	return &DeleteLeakingEBSJob{
		ec2API:     ec2API,
		filterTags: tags,
	}
}

func (j *DeleteLeakingEBSJob) validate() error {
	if len(j.filterTags) == 0 {
		return errors.New("filterTags is empty")
	}
	return nil
}

func (j *DeleteLeakingEBSJob) listMayLeakingVolumes() ([]*ec2.Volume, error) {
	// filter on status=available
	// tags:KubernetesCluster=clinic-prod or tag:KubernetesCluster=clinic-staging
	curr := time.Now()
	filters := []*ec2.Filter{
		{
			Name: aws.String("status"),
			Values: []*string{
				aws.String("available"),
			},
		},
	}
	for k, v := range j.filterTags {
		filters = append(filters, &ec2.Filter{
			Name:   aws.String("tag:" + k),
			Values: []*string{aws.String(v)},
		})
	}
	input := &ec2.DescribeVolumesInput{
		MaxResults: aws.Int64(500),
		Filters:    filters,
	}
	output, err := j.ec2API.DescribeVolumes(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			log.Error("describe volumes failed", zap.String("code", aerr.Code()), zap.String("message", aerr.Message()), zap.Error(aerr))
		} else {
			log.Error("describe volumes failed", zap.Error(err))
		}
		return nil, err
	}
	result := make([]*ec2.Volume, 0)
	for _, volume := range output.Volumes {
		// filter out volumes created in recent 1 day
		if volume.CreateTime.Unix()-curr.Unix() > 24*3600 {
			continue
		}
		result = append(result, volume)
	}

	return result, nil
}

func (j *DeleteLeakingEBSJob) deleteVolumes(volumes []*ec2.Volume) {
	for _, volume := range volumes {
		input := &ec2.DeleteVolumeInput{VolumeId: volume.VolumeId}
		if _, err := j.ec2API.DeleteVolume(input); err != nil {
			// just log error, continue to delete other volumes
			if aerr, ok := err.(awserr.Error); ok {
				log.Error("describe volumes failed", zap.String("code", aerr.Code()), zap.String("message", aerr.Message()), zap.Error(aerr))
			} else {
				log.Error("describe volumes failed", zap.Error(err))
			}
		}
	}
}

func (j *DeleteLeakingEBSJob) Run() {
	log.Info("corn job running", zap.String("name", "DeleteLeakingEBSJob"))
	// make sure filter tag is set
	if j.validate() != nil {
		log.Error("validate failed, skip running", zap.Error(j.validate()))
		return
	}
	volumes, err := j.listMayLeakingVolumes()
	if err != nil {
		log.Error("list may leaking volumes failed", zap.Error(err))
		return
	}
	log.Warn("found may leaking volumes", zap.Int("count", len(volumes)))
	j.deleteVolumes(volumes)
}
