package service

import (
	"context"
	"sort"
	"strings"
	"time"
	"unicode"

	"github.com/1Panel-dev/1Panel/agent/app/dto"
	"github.com/1Panel-dev/1Panel/agent/buserr"
	"github.com/1Panel-dev/1Panel/agent/utils/common"
	"github.com/1Panel-dev/1Panel/agent/utils/docker"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/volume"
)

func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{}, error) {
	client, err := docker.NewDockerClient()
	if err != nil {
		return 0, nil, err
	}
	list, err := client.VolumeList(context.TODO(), volume.ListOptions{})
	if err != nil {
		return 0, nil, err
	}
	if len(req.Info) != 0 {
		length, count := len(list.Volumes), 0
		for count < length {
			if !strings.Contains(list.Volumes[count].Name, req.Info) {
				list.Volumes = append(list.Volumes[:count], list.Volumes[(count+1):]...)
				length--
			} else {
				count++
			}
		}
	}
	var (
		data    []dto.Volume
		records []*volume.Volume
	)
	sort.Slice(list.Volumes, func(i, j int) bool {
		return list.Volumes[i].CreatedAt > list.Volumes[j].CreatedAt
	})
	total, start, end := len(list.Volumes), (req.Page-1)*req.PageSize, req.Page*req.PageSize
	if start > total {
		records = make([]*volume.Volume, 0)
	} else {
		if end >= total {
			end = total
		}
		records = list.Volumes[start:end]
	}

	nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
	for _, item := range records {
		var volume dto.Volume
		volume.Driver = item.Driver
		volume.Mountpoint = item.Mountpoint
		volume.Name = simplifyVolumeName(item.Name)
		for key, val := range item.Labels {
			volume.Labels = append(volume.Labels, dto.VolumeOption{Key: key, Value: val})
		}
		for key, val := range item.Options {
			volume.Options = append(volume.Options, dto.VolumeOption{Key: key, Value: val})
		}
		sort.Slice(volume.Options, func(i, j int) bool {
			return volume.Options[i].Key < volume.Options[j].Key
		})
		if strings.Contains(item.CreatedAt, "Z") {
			volume.CreatedAt, _ = time.ParseInLocation("2006-01-02T15:04:05Z", item.CreatedAt, nyc)
		} else if strings.Contains(item.CreatedAt, "+") {
			volume.CreatedAt, _ = time.ParseInLocation("2006-01-02T15:04:05+08:00", item.CreatedAt, nyc)
		} else {
			volume.CreatedAt, _ = time.ParseInLocation("2006-01-02T15:04:05", item.CreatedAt, nyc)
		}
		data = append(data, volume)
	}

	return int64(total), data, nil
}
func (u *ContainerService) ListVolume() ([]dto.Options, error) {
	client, err := docker.NewDockerClient()
	if err != nil {
		return nil, err
	}
	defer client.Close()
	list, err := client.VolumeList(context.TODO(), volume.ListOptions{})
	if err != nil {
		return nil, err
	}
	var datas []dto.Options
	for _, item := range list.Volumes {
		datas = append(datas, dto.Options{
			Option: item.Name,
		})
	}
	sort.Slice(datas, func(i, j int) bool {
		return datas[i].Option < datas[j].Option
	})
	return datas, nil
}
func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
	client, err := docker.NewDockerClient()
	if err != nil {
		return err
	}
	defer client.Close()
	for _, id := range req.Names {
		if err := client.VolumeRemove(context.TODO(), id, true); err != nil {
			if strings.Contains(err.Error(), "volume is in use") {
				return buserr.WithDetail("ErrInUsed", id, nil)
			}
			return err
		}
	}
	return nil
}
func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
	client, err := docker.NewDockerClient()
	if err != nil {
		return err
	}
	defer client.Close()
	arg := filters.NewArgs()
	arg.Add("name", req.Name)
	vos, _ := client.VolumeList(context.TODO(), volume.ListOptions{Filters: arg})
	if len(vos.Volumes) != 0 {
		for _, v := range vos.Volumes {
			if v.Name == req.Name {
				return buserr.New("ErrRecordExist")
			}
		}
	}
	options := volume.CreateOptions{
		Name:       req.Name,
		Driver:     req.Driver,
		DriverOpts: stringsToMap(req.Options),
		Labels:     stringsToMap(req.Labels),
	}
	if _, err := client.VolumeCreate(context.TODO(), options); err != nil {
		return err
	}
	return nil
}

func simplifyVolumeName(name string) string {
	if len(name) != 64 {
		return name
	}

	for _, char := range name {
		if !unicode.Is(unicode.ASCII_Hex_Digit, char) {
			return name
		}
	}
	return name[:12]
}
