package docker

import (
	"context"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"

	"gitee.ltd/lxh/wechat-robot/internal/config"
)

// ContainerInfo 容器信息
type ContainerInfo struct {
	ID      string            `json:"id"`
	Name    string            `json:"name"`
	Image   string            `json:"image"`
	Status  string            `json:"status"`
	Created int64             `json:"created"`
	Labels  map[string]string `json:"labels"`
}

// CreateContainer 创建容器
func CreateContainer(ctx context.Context, cfg *config.DockerConfig, name string, env []string, labels map[string]string, port int) (string, error) {
	cli := GetClient()

	// 检测是否在容器内运行
	inContainer := isRunningInContainer()

	// 如果在容器内运行，获取当前容器的网络设置
	var currentNetworkName string
	if inContainer {
		currentNetworkName = getCurrentContainerNetwork(ctx, cli)
		if currentNetworkName != "" {
			// 使用发现的网络替代配置中的网络
			cfg.Network = currentNetworkName
		}
	}

	// 端口映射 - 将容器的9000端口映射到主机的端口
	hostPort := nat.Port("9000/tcp")
	exposedPorts := nat.PortSet{
		hostPort: struct{}{},
	}

	// 设置主机端口映射
	portBindings := nat.PortMap{}

	// 只有在非容器环境下才进行端口映射
	if !inContainer {
		// 如果没有指定端口，则自动分配
		if port <= 0 {
			// 查找同镜像容器的最大端口号并加1
			maxPort, err := findMaxPortForImage(ctx, cli, cfg.ImageName)
			if err != nil {
				// 如果出错，使用默认端口9001
				port = 9001
			} else {
				port = maxPort + 1
			}
		}

		// 添加端口映射
		portBindings[hostPort] = []nat.PortBinding{
			{
				HostIP:   "0.0.0.0",
				HostPort: strconv.Itoa(port),
			},
		}
	}

	// 添加Redis环境变量
	if cfg.Redis.Host != "" {
		env = append(env, fmt.Sprintf("REDIS_HOST=%s", cfg.Redis.Host))
		env = append(env, fmt.Sprintf("REDIS_PASSWORD=%s", cfg.Redis.Password))
		env = append(env, fmt.Sprintf("REDIS_DB=%d", cfg.Redis.DB))
	}

	// 设置容器配置
	containerConfig := &container.Config{
		Image:        cfg.ImageName,
		Env:          env,
		ExposedPorts: exposedPorts,
		Labels:       labels,
	}

	// 设置主机配置
	hostConfig := &container.HostConfig{
		PortBindings: portBindings,
		RestartPolicy: container.RestartPolicy{
			Name: "unless-stopped",
		},
	}
	if cfg.Memory == 0 {
		cfg.Memory = 512 // 默认内存限制为512M
	}
	hostConfig.Memory = cfg.Memory * 1024 * 1024 // 限制使用内存

	// 设置网络配置
	networkingConfig := &network.NetworkingConfig{}
	if cfg.Network != "" {
		// 首先检查网络类型，只在用户自定义网络上分配固定IP
		isUserNetwork := false
		if cfg.Network != "bridge" && cfg.Network != "host" && cfg.Network != "none" {
			// 检查网络是否存在
			_, err := cli.NetworkInspect(ctx, cfg.Network, network.InspectOptions{})
			if err == nil {
				isUserNetwork = true
			}
		}

		endpointsConfig := make(map[string]*network.EndpointSettings)
		endpointSettings := &network.EndpointSettings{}

		// 只在用户自定义网络上尝试分配固定IP
		if isUserNetwork {
			// 自动为容器分配一个递增的IP地址
			nextIP, err := getNextAvailableIPInNetwork(ctx, cli, cfg.Network)
			if err == nil && nextIP != "" {
				endpointSettings.IPAMConfig = &network.EndpointIPAMConfig{
					IPv4Address: nextIP,
				}
			}
		}

		endpointsConfig[cfg.Network] = endpointSettings
		networkingConfig.EndpointsConfig = endpointsConfig
	}

	// 创建容器
	resp, err := cli.ContainerCreate(
		ctx,
		containerConfig,
		hostConfig,
		networkingConfig,
		nil, // 平台
		name,
	)

	if err != nil {
		return "", err
	}

	return resp.ID, nil
}

// getNextAvailableIPInNetwork 获取网络中下一个可用的IP地址
func getNextAvailableIPInNetwork(ctx context.Context, cli *client.Client, networkName string) (string, error) {
	// 获取网络信息
	networkResource, err := cli.NetworkInspect(ctx, networkName, network.InspectOptions{})
	if err != nil {
		return "", fmt.Errorf("无法检查网络: %w", err)
	}

	// 确认网络是否有配置子网
	if len(networkResource.IPAM.Config) == 0 {
		return "", fmt.Errorf("网络没有IPAM配置")
	}

	// 获取网络子网
	subnet := networkResource.IPAM.Config[0].Subnet
	if subnet == "" {
		return "", fmt.Errorf("网络子网未定义，不能分配固定IP")
	}

	// 解析子网
	_, ipNet, err := net.ParseCIDR(subnet)
	if err != nil {
		return "", fmt.Errorf("无法解析子网CIDR: %w", err)
	}

	// 获取网络中的所有容器
	containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
	if err != nil {
		return "", fmt.Errorf("无法列出容器: %w", err)
	}

	// 获取已使用的IP地址
	usedIPs := make(map[string]bool)
	for _, c := range containers {
		for name, network := range c.NetworkSettings.Networks {
			if name == networkName && network.IPAddress != "" {
				usedIPs[network.IPAddress] = true
			}
		}
	}

	// 获取网络中的所有IP地址，查找最大IP
	var maxIP net.IP
	for ip := range usedIPs {
		ipAddr := net.ParseIP(ip)
		if ipAddr == nil {
			continue
		}

		// 确保IP在子网内
		if !ipNet.Contains(ipAddr) {
			continue
		}

		// 更新最大IP
		if maxIP == nil || compareIPs(ipAddr, maxIP) > 0 {
			maxIP = ipAddr
		}
	}

	// 如果网络中没有容器，使用网关地址的下一个IP
	if maxIP == nil {
		// 获取网关IP
		gateway := networkResource.IPAM.Config[0].Gateway
		if gateway == "" {
			// 如果没有定义网关，使用子网的第一个可用IP
			ip := ipNet.IP.To4()
			if ip == nil {
				return "", fmt.Errorf("无法获取IPv4地址")
			}

			// 通常第一个可用IP是网关后的地址（x.x.x.1 + 1 = x.x.x.2）
			ip[3]++
			maxIP = ip
		} else {
			gwIP := net.ParseIP(gateway)
			if gwIP == nil {
				return "", fmt.Errorf("无效的网关地址")
			}

			// 使用网关IP的下一个地址
			maxIP = gwIP.To4()
			if maxIP == nil {
				return "", fmt.Errorf("无法获取IPv4网关地址")
			}
			maxIP[3]++
		}
	} else {
		// 如果有容器，则使用最大IP + 1
		maxIP = incrementIP(maxIP)
	}

	// 确保新IP仍然在子网内
	if !ipNet.Contains(maxIP) {
		return "", fmt.Errorf("无法分配下一个IP，已达到子网范围上限")
	}

	return maxIP.String(), nil
}

// compareIPs 比较两个IP地址的大小
func compareIPs(a, b net.IP) int {
	// 转为IPv4格式
	a4 := a.To4()
	b4 := b.To4()

	if a4 == nil || b4 == nil {
		// 如果任一IP不是IPv4，则无法比较
		return 0
	}

	// 逐字节比较
	for i := 0; i < 4; i++ {
		if a4[i] < b4[i] {
			return -1
		} else if a4[i] > b4[i] {
			return 1
		}
	}
	return 0
}

// incrementIP 将IP地址递增1
func incrementIP(ip net.IP) net.IP {
	// 创建IP的副本
	newIP := make(net.IP, len(ip))
	copy(newIP, ip)

	// 确保使用IPv4格式
	ipv4 := newIP.To4()
	if ipv4 == nil {
		return newIP
	}

	// 递增最后一个字节
	for i := 3; i >= 0; i-- {
		ipv4[i]++
		if ipv4[i] > 0 {
			// 如果没有溢出，退出循环
			break
		}
		// 如果溢出（变成0），继续递增上一个字节
	}

	return ipv4
}

// getNextAvailableIP 获取网络中下一个可用的IP地址
func getNextAvailableIP(ctx context.Context, cli *client.Client, networkName string) (string, error) {
	// 获取网络信息
	networkResource, err := cli.NetworkInspect(ctx, networkName, network.InspectOptions{})
	if err != nil {
		return "", fmt.Errorf("无法检查网络: %w", err)
	}

	// 确认网络类型是否为bridge，并获取子网信息
	if networkResource.IPAM.Config == nil || len(networkResource.IPAM.Config) == 0 {
		return "", fmt.Errorf("网络没有IPAM配置")
	}

	// 获取网络子网
	subnet := networkResource.IPAM.Config[0].Subnet
	if subnet == "" {
		return "", fmt.Errorf("网络子网未定义")
	}

	// 解析子网
	_, ipNet, err := net.ParseCIDR(subnet)
	if err != nil {
		return "", fmt.Errorf("无法解析子网CIDR: %w", err)
	}

	// 获取现有容器的IP地址
	existingIPs := make(map[string]bool)
	containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
	if err != nil {
		return "", fmt.Errorf("无法列出容器: %w", err)
	}

	// 收集所有容器在这个网络中使用的IP
	for _, c := range containers {
		if c.NetworkSettings != nil {
			for name, network := range c.NetworkSettings.Networks {
				if name == networkName && network.IPAddress != "" {
					existingIPs[network.IPAddress] = true
				}
			}
		}
	}

	// 确定最大IP地址
	var maxIP net.IP
	for ipStr := range existingIPs {
		ip := net.ParseIP(ipStr)
		if ip == nil {
			continue
		}

		if !ipNet.Contains(ip) {
			continue // IP不在子网内
		}

		if maxIP == nil || compareIPs(ip, maxIP) > 0 {
			maxIP = ip
		}
	}

	// 如果没有找到最大IP，使用子网的第一个可用IP（通常是x.x.x.2，因为x.x.x.1通常是网关）
	if maxIP == nil {
		firstIP := getFirstUsableIP(ipNet)
		if firstIP != nil {
			return firstIP.String(), nil
		}
		return "", fmt.Errorf("无法确定子网的第一个可用IP")
	}

	// 递增最大IP地址
	nextIP := getNextIP(maxIP)

	// 确保新IP仍然在子网内
	if !ipNet.Contains(nextIP) {
		return "", fmt.Errorf("无法分配下一个IP，已达到子网范围上限")
	}

	return nextIP.String(), nil
}

// getNextIP 获取下一个IP地址
func getNextIP(ip net.IP) net.IP {
	// 创建IP的副本
	nextIP := make(net.IP, len(ip))
	copy(nextIP, ip)

	// 确保使用IPv4格式
	nextIP = nextIP.To4()
	if nextIP == nil {
		return nil
	}

	// 从最后一个字节开始递增
	for i := len(nextIP) - 1; i >= 0; i-- {
		if nextIP[i] < 255 {
			nextIP[i]++
			break
		}
		nextIP[i] = 0 // 进位
	}

	return nextIP
}

// getFirstUsableIP 获取子网中的第一个可用IP
func getFirstUsableIP(ipNet *net.IPNet) net.IP {
	// 子网的第一个地址通常是网络地址，不可用
	// 第二个地址通常是网关地址，也可能不可用
	// 因此我们从第三个地址开始（通常是x.x.x.2）

	// 获取子网的起始IP
	ip := ipNet.IP.To4()
	if ip == nil {
		return nil
	}

	// 创建副本
	firstIP := make(net.IP, len(ip))
	copy(firstIP, ip)

	// 递增到第三个地址
	thirdOctet := int(firstIP[2])
	fourthOctet := int(firstIP[3]) + 2 // 通常是x.x.x.2

	// 处理进位
	if fourthOctet > 255 {
		fourthOctet = fourthOctet % 256
		thirdOctet++
		if thirdOctet > 255 {
			// 溢出处理（通常不会发生在正常子网中）
			return nil
		}
	}

	firstIP[2] = byte(thirdOctet)
	firstIP[3] = byte(fourthOctet)

	return firstIP
}

// getCurrentContainerNetwork 获取当前容器所在的网络
func getCurrentContainerNetwork(ctx context.Context, cli *client.Client) string {
	// 获取当前容器的ID
	hostname, err := os.Hostname()
	if err != nil {
		return ""
	}

	// 查询容器信息
	containerInfo, err := cli.ContainerInspect(ctx, hostname)
	if err != nil {
		return ""
	}

	// 遍历容器的网络配置，获取第一个非空网络名称
	for networkName := range containerInfo.NetworkSettings.Networks {
		if networkName != "bridge" && networkName != "host" && networkName != "none" {
			return networkName // 返回自定义网络名称
		}
	}

	// 如果只有默认网络，则返回第一个网络名称
	for networkName := range containerInfo.NetworkSettings.Networks {
		return networkName // 返回第一个找到的网络名称
	}

	return ""
}

// isRunningInContainer 检测当前程序是否在容器内运行
func isRunningInContainer() bool {
	// 检查环境变量 IS_DOCKER
	isDockEnv := os.Getenv("IS_DOCKER")
	if isDockEnv == "true" || isDockEnv == "1" || isDockEnv == "yes" {
		return true
	}

	// 通过检查cgroup文件判断
	if _, err := os.Stat("/.dockerenv"); err == nil {
		return true
	}

	// 检查进程的cgroup信息
	if data, err := os.ReadFile("/proc/1/cgroup"); err == nil {
		return strings.Contains(string(data), "docker")
	}

	return false
}

// findMaxPortForImage 查找指定镜像的容器已使用的最大端口号
func findMaxPortForImage(ctx context.Context, cli *client.Client, imageName string) (int, error) {
	// 默认起始端口
	maxPort := 9000

	// 获取所有容器
	containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
	if err != nil {
		return maxPort, err
	}

	// 遍历容器，查找使用相同镜像且端口映射最大的容器
	for _, c := range containers {
		if c.Image == imageName || strings.HasPrefix(c.Image, imageName+":") {
			for _, port := range c.Ports {
				// 找到与内部9000端口映射的主机端口
				if port.PrivatePort == 9000 && int(port.PublicPort) > maxPort {
					maxPort = int(port.PublicPort)
				}
			}
		}
	}

	return maxPort, nil
}

// StartContainer 启动容器
func StartContainer(ctx context.Context, containerID string) error {
	cli := GetClient()
	return cli.ContainerStart(ctx, containerID, container.StartOptions{})
}

// StopContainer 停止容器
func StopContainer(ctx context.Context, containerID string, timeout *time.Duration) error {
	cli := GetClient()
	t := int(timeout.Seconds())
	return cli.ContainerStop(ctx, containerID, container.StopOptions{Timeout: &t})
}

// RemoveContainer 删除容器
func RemoveContainer(ctx context.Context, containerID string, force bool) error {
	cli := GetClient()
	return cli.ContainerRemove(ctx, containerID, container.RemoveOptions{
		Force: force,
	})
}

// ListContainers 列出容器
func ListContainers(ctx context.Context, filterArgs map[string][]string) ([]ContainerInfo, error) {
	cli := GetClient()

	// 构建过滤器
	filterSet := filters.NewArgs()
	for k, vals := range filterArgs {
		for _, v := range vals {
			filterSet.Add(k, v)
		}
	}

	containers, err := cli.ContainerList(ctx, container.ListOptions{
		All:     true, // 包括未运行的容器
		Filters: filterSet,
	})
	if err != nil {
		return nil, err
	}

	var containerInfos []ContainerInfo
	for _, c := range containers {
		containerInfos = append(containerInfos, ContainerInfo{
			ID:      c.ID,
			Name:    c.Names[0][1:], // 去掉前面的/
			Image:   c.Image,
			Status:  c.Status,
			Created: c.Created,
			Labels:  c.Labels,
		})
	}

	return containerInfos, nil
}

// GetContainerLogs 获取容器日志
func GetContainerLogs(ctx context.Context, containerID string, tail string) (string, error) {
	cli := GetClient()

	options := container.LogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Tail:       tail,
	}

	logs, err := cli.ContainerLogs(ctx, containerID, options)
	if err != nil {
		return "", err
	}
	defer logs.Close()

	// 读取日志内容
	logContent, err := io.ReadAll(logs)
	if err != nil {
		return "", err
	}

	return string(logContent), nil
}

// GetContainerStatus 获取容器状态
func GetContainerStatus(ctx context.Context, containerID string) (string, error) {
	cli := GetClient()

	inspect, err := cli.ContainerInspect(ctx, containerID)
	if err != nil {
		return "", err
	}

	if inspect.State.Running {
		return "running", nil
	} else if inspect.State.Paused {
		return "paused", nil
	} else if inspect.State.Restarting {
		return "restarting", nil
	} else {
		return "stopped", nil
	}
}

// WaitForContainer 等待容器达到指定状态
func WaitForContainer(ctx context.Context, containerID string) (<-chan container.WaitResponse, <-chan error) {
	cli := GetClient()
	return cli.ContainerWait(ctx, containerID, container.WaitConditionNotRunning)
}

// GetContainerIP 获取容器的IP地址
func GetContainerIP(ctx context.Context, containerID string, cfg *config.DockerConfig) (string, error) {
	// 如果在Docker环境中运行，需要获取容器真实IP
	if isRunningInContainer() {
		cli := GetClient()

		inspect, err := cli.ContainerInspect(ctx, containerID)
		if err != nil {
			return "", fmt.Errorf("无法检查容器: %w", err)
		}

		// 遍历网络配置找到IP地址
		for networkName, network := range inspect.NetworkSettings.Networks {
			if network.IPAddress != "" {
				return network.IPAddress, nil
			}
			// 防止在没有获取到IP的情况下继续循环
			fmt.Printf("Network %s has no IP address\n", networkName)
		}
		return "localhost", fmt.Errorf("未找到容器IP地址")
	} else {
		// 如果不是在Docker环境中运行，使用配置中的主机地址
		return extractHostIP(cfg.Host), nil
	}
}

// GetContainerHost 获取容器的访问地址（格式：ip:port）
func GetContainerHost(ctx context.Context, containerID string, cfg *config.DockerConfig) (string, error) {
	// 如果在Docker环境中运行，需要获取容器真实IP或容器名
	if isRunningInContainer() {
		cli := GetClient()

		inspect, err := cli.ContainerInspect(ctx, containerID)
		if err != nil {
			return "", fmt.Errorf("无法检查容器: %w", err)
		}

		// 使用容器名作为主机名，用于内部网络通信
		containerName := inspect.Name
		if containerName != "" {
			// 移除前导斜杠
			if strings.HasPrefix(containerName, "/") {
				containerName = containerName[1:]
			}
			// 使用容器名:9000作为内部访问地址
			return containerName + ":9000", nil
		}

		// 备选方案：尝试使用容器IP
		for _, network := range inspect.NetworkSettings.Networks {
			if network.IPAddress != "" {
				// 容器内部访问使用IP:9000格式
				return network.IPAddress + ":9000", nil
			}
		}

		return "localhost:9000", fmt.Errorf("未找到容器名或IP地址")
	} else {
		// 如果不是在Docker环境中运行，需要获取端口映射
		cli := GetClient()

		inspect, err := cli.ContainerInspect(ctx, containerID)
		if err != nil {
			return "", fmt.Errorf("无法检查容器: %w", err)
		}

		hostIP := extractHostIP(cfg.Host)

		// 查找9000端口的映射
		for _, port := range inspect.NetworkSettings.Ports["9000/tcp"] {
			if port.HostPort != "" {
				// 主机访问使用hostIP:映射端口格式
				return hostIP + ":" + port.HostPort, nil
			}
		}

		// 如果找不到端口映射，返回默认的localhost:9000
		return hostIP + ":9000", nil
	}
}

// extractHostIP 从Docker主机地址中提取IP
func extractHostIP(host string) string {
	// 处理http格式URL
	if strings.HasPrefix(host, "http://") {
		host = strings.TrimPrefix(host, "http://")
	} else if strings.HasPrefix(host, "https://") {
		host = strings.TrimPrefix(host, "https://")
	} else if strings.HasPrefix(host, "tcp://") {
		host = strings.TrimPrefix(host, "tcp://")
	}

	// 如果是unix socket或空值，返回localhost
	if host == "" || strings.HasPrefix(host, "unix://") {
		return "localhost"
	}

	// 分离端口号
	if index := strings.LastIndex(host, ":"); index != -1 {
		host = host[:index]
	}

	return host
}
