package data

import (
	"fmt"
	"slices"
	"strings"
	"time"

	"github.com/go-resty/resty/v2"

	"github.com/tnb-labs/panel/internal/biz"
	"github.com/tnb-labs/panel/internal/http/request"
	"github.com/tnb-labs/panel/pkg/shell"
	"github.com/tnb-labs/panel/pkg/types"
	"github.com/tnb-labs/panel/pkg/types/docker/container"
)

type containerRepo struct {
	client *resty.Client
}

func NewContainerRepo() biz.ContainerRepo {
	return &containerRepo{
		client: getDockerClient("/var/run/docker.sock"),
	}
}

// ListAll 列出所有容器
func (r *containerRepo) ListAll() ([]types.Container, error) {
	var resp []container.Container
	_, err := r.client.R().SetResult(&resp).SetQueryParam("all", "true").Get("/containers/json")
	if err != nil {
		return nil, err
	}

	var containers []types.Container
	for _, item := range resp {
		ports := make([]types.ContainerPort, 0)
		for _, port := range item.Ports {
			ports = append(ports, types.ContainerPort{
				ContainerStart: uint(port.PrivatePort),
				ContainerEnd:   uint(port.PrivatePort),
				HostStart:      uint(port.PublicPort),
				HostEnd:        uint(port.PublicPort),
				Protocol:       port.Type,
				Host:           port.IP,
			})
		}
		if len(item.Names) == 0 {
			item.Names = append(item.Names, "")
		}
		containers = append(containers, types.Container{
			ID:        item.ID,
			Name:      strings.TrimPrefix(item.Names[0], "/"), // https://github.com/moby/moby/issues/7519
			Image:     item.Image,
			ImageID:   item.ImageID,
			Command:   item.Command,
			CreatedAt: time.Unix(item.Created, 0),
			State:     item.State,
			Status:    item.Status,
			Ports:     ports,
			Labels:    types.MapToKV(item.Labels),
		})
	}

	slices.SortFunc(containers, func(a types.Container, b types.Container) int {
		return strings.Compare(a.Name, b.Name)
	})

	return containers, nil
}

// ListByName 根据名称搜索容器
func (r *containerRepo) ListByName(names string) ([]types.Container, error) {
	containers, err := r.ListAll()
	if err != nil {
		return nil, err
	}

	containers = slices.DeleteFunc(containers, func(item types.Container) bool {
		return !strings.Contains(item.Name, names)
	})

	return containers, nil
}

// Create 创建容器
func (r *containerRepo) Create(req *request.ContainerCreate) (string, error) {
	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("docker run -d --name %s", req.Name))
	if req.PublishAllPorts {
		sb.WriteString(" -P")
	} else {
		for _, port := range req.Ports {
			sb.WriteString(" -p ")
			if port.Host != "" {
				sb.WriteString(fmt.Sprintf("%s:", port.Host))
			}
			if port.HostStart == port.HostEnd || port.ContainerStart == port.ContainerEnd {
				sb.WriteString(fmt.Sprintf("%d:%d/%s", port.HostStart, port.ContainerStart, port.Protocol))
			} else {
				sb.WriteString(fmt.Sprintf("%d-%d:%d-%d/%s", port.HostStart, port.HostEnd, port.ContainerStart, port.ContainerEnd, port.Protocol))
			}
		}
	}
	if req.Network != "" {
		sb.WriteString(fmt.Sprintf(" --network %s", req.Network))
	}
	for _, volume := range req.Volumes {
		sb.WriteString(fmt.Sprintf(" -v %s:%s:%s", volume.Host, volume.Container, volume.Mode))
	}
	for _, label := range req.Labels {
		sb.WriteString(fmt.Sprintf(" --label %s=%s", label.Key, label.Value))
	}
	for _, env := range req.Env {
		sb.WriteString(fmt.Sprintf(" -e %s=%s", env.Key, env.Value))
	}
	if len(req.Entrypoint) > 0 {
		sb.WriteString(fmt.Sprintf(" --entrypoint '%s'", strings.Join(req.Entrypoint, " ")))
	}
	if len(req.Command) > 0 {
		sb.WriteString(fmt.Sprintf(" '%s'", strings.Join(req.Command, " ")))
	}
	if req.RestartPolicy != "" {
		sb.WriteString(fmt.Sprintf(" --restart %s", req.RestartPolicy))
	}
	if req.AutoRemove {
		sb.WriteString(" --rm")
	}
	if req.Privileged {
		sb.WriteString(" --privileged")
	}
	if req.OpenStdin {
		sb.WriteString(" -i")
	}
	if req.Tty {
		sb.WriteString(" -t")
	}
	if req.CPUShares > 0 {
		sb.WriteString(fmt.Sprintf(" --cpu-shares %d", req.CPUShares))
	}
	if req.CPUs > 0 {
		sb.WriteString(fmt.Sprintf(" --cpus %d", req.CPUs))
	}
	if req.Memory > 0 {
		sb.WriteString(fmt.Sprintf(" --memory %d", req.Memory))
	}

	sb.WriteString(" %s bash")
	return shell.ExecfWithTTY(sb.String(), req.Image)
}

// Remove 移除容器
func (r *containerRepo) Remove(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker rm -f %s", id)
	return err
}

// Start 启动容器
func (r *containerRepo) Start(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker start %s", id)
	return err
}

// Stop 停止容器
func (r *containerRepo) Stop(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker stop %s", id)
	return err
}

// Restart 重启容器
func (r *containerRepo) Restart(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker restart %s", id)
	return err
}

// Pause 暂停容器
func (r *containerRepo) Pause(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker pause %s", id)
	return err
}

// Unpause 恢复容器
func (r *containerRepo) Unpause(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker unpause %s", id)
	return err
}

// Kill 杀死容器
func (r *containerRepo) Kill(id string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker kill %s", id)
	return err
}

// Rename 重命名容器
func (r *containerRepo) Rename(id string, newName string) error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker rename %s %s", id, newName)
	return err
}

// Logs 查看容器日志
func (r *containerRepo) Logs(id string) (string, error) {
	return shell.ExecfWithTimeout(2*time.Minute, "docker logs --tail 100 %s", id)
}

// Prune 清理未使用的容器
func (r *containerRepo) Prune() error {
	_, err := shell.ExecfWithTimeout(2*time.Minute, "docker container prune -f")
	return err
}
