package agent

import (
	"context"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"

	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/image"
	"github.com/docker/docker/client"
)

// Docker 工具：通过 Docker API 操作本地或远程 Docker。
// socket 地址通过 DockerConfigStore 动态读取，用户在 Web 页面配置后即时生效。

func (t *Toolset) dockerClient() (*client.Client, error) {
	socket := t.dockerSocket()
	cli, err := client.NewClientWithOpts(
		client.WithHost(socket),
		client.WithAPIVersionNegotiation(),
	)
	if err != nil {
		return nil, fmt.Errorf("创建 Docker 客户端失败: %w", err)
	}
	return cli, nil
}

func (t *Toolset) dockerPS(ctx context.Context, params map[string]any) ToolResult {
	all, _ := params["all"].(bool)
	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	list, err := cli.ContainerList(ctx, container.ListOptions{All: all})
	if err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	if len(list) == 0 {
		return ToolResult{Success: true, Output: "没有找到容器"}
	}

	sort.Slice(list, func(i, j int) bool {
		return list[i].Created > list[j].Created
	})
	var out strings.Builder
	fmt.Fprintf(&out, "%-14s %-26s %-20s %-12s %s\n", "CONTAINER ID", "NAME", "IMAGE", "STATUS", "PORTS")
	for _, c := range list {
		name := strings.TrimPrefix(strings.Join(c.Names, ","), "/")
		fmt.Fprintf(&out, "%-14s %-26s %-20s %-12s %s\n",
			c.ID[:min(12, len(c.ID))], name, c.Image, c.Status, dockerPorts(c.Ports))
	}
	return ToolResult{Success: true, Output: t.truncate(out.String())}
}

func (t *Toolset) dockerImages(ctx context.Context, params map[string]any) ToolResult {
	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	list, err := cli.ImageList(ctx, image.ListOptions{All: true})
	if err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	if len(list) == 0 {
		return ToolResult{Success: true, Output: "没有找到镜像"}
	}

	sort.Slice(list, func(i, j int) bool {
		return list[i].Created > list[j].Created
	})
	var out strings.Builder
	fmt.Fprintf(&out, "%-22s %-28s %-14s %s\n", "REPOSITORY", "TAG", "IMAGE ID", "SIZE")
	for _, img := range list {
		if len(img.RepoTags) == 0 {
			continue
		}
		for _, tag := range img.RepoTags {
			repo, imageTag, _ := strings.Cut(tag, ":")
			id := img.ID
			if len(id) > 12 {
				id = id[7:19]
			}
			fmt.Fprintf(&out, "%-22s %-28s %-14s %s\n", repo, imageTag, id, formatBytes(img.Size))
		}
	}
	return ToolResult{Success: true, Output: t.truncate(out.String())}
}

func (t *Toolset) dockerLogs(ctx context.Context, params map[string]any) ToolResult {
	containerID, _ := params["container"].(string)
	containerID = strings.TrimSpace(containerID)
	if containerID == "" {
		return ToolResult{Success: false, Output: "container 不能为空"}
	}
	tail := intParam(params, "tail")
	if tail <= 0 {
		tail = 100
	}

	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	resp, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Tail:       strconv.Itoa(tail),
	})
	if err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	defer resp.Close()

	raw, err := io.ReadAll(resp)
	if err != nil {
		return ToolResult{Success: false, Output: "读取 Docker 日志失败: " + err.Error()}
	}
	return ToolResult{Success: true, Output: t.truncate(demuxDockerLogs(raw))}
}

// demuxDockerLogs 解析 Docker 日志流：多路复用流（stdout/stderr 分帧）按帧解出，
// 普通文本流（TTY 容器）直接返回。
func demuxDockerLogs(data []byte) string {
	if len(data) >= 8 && isMultiplexedFrame(data[0:8]) {
		var out strings.Builder
		for i := 0; i+8 <= len(data); {
			frameSize := int(binary.BigEndian.Uint32(data[i+4 : i+8]))
			i += 8
			if frameSize < 0 || i+frameSize > len(data) {
				break
			}
			out.Write(data[i : i+frameSize])
			i += frameSize
		}
		return out.String()
	}
	return string(data)
}

func isMultiplexedFrame(header []byte) bool {
	streamType := header[0]
	if streamType != 0 && streamType != 1 && streamType != 2 {
		return false
	}
	return header[1] == 0 && header[2] == 0 && header[3] == 0
}

func (t *Toolset) dockerInspect(ctx context.Context, params map[string]any) ToolResult {
	containerID, _ := params["container"].(string)
	containerID = strings.TrimSpace(containerID)
	if containerID == "" {
		return ToolResult{Success: false, Output: "container 不能为空"}
	}

	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	inspect, err := cli.ContainerInspect(ctx, containerID)
	if err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}

	state := inspect.State
	if state == nil {
		state = &container.State{}
	}
	summary := map[string]any{
		"id":            inspect.ID,
		"name":          strings.TrimPrefix(inspect.Name, "/"),
		"image":         inspect.Config.Image,
		"status":        state.Status,
		"running":       state.Running,
		"exit_code":     state.ExitCode,
		"restart_count": inspect.RestartCount,
		"created":       inspect.Created,
		"command":       inspect.Config.Cmd,
		"env_count":     len(inspect.Config.Env),
		"ports":         inspect.NetworkSettings.Ports,
		"network_mode":  string(inspect.HostConfig.NetworkMode),
		"ip_address":    inspect.NetworkSettings.IPAddress,
	}
	data, _ := json.MarshalIndent(summary, "", "  ")
	return ToolResult{Success: true, Output: t.truncate(string(data))}
}

func (t *Toolset) dockerExec(ctx context.Context, params map[string]any) ToolResult {
	containerID, _ := params["container"].(string)
	containerID = strings.TrimSpace(containerID)
	command, _ := params["command"].(string)
	command = strings.TrimSpace(command)
	if containerID == "" || command == "" {
		return ToolResult{Success: false, Output: "container 和 command 不能为空"}
	}

	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	execCfg := container.ExecOptions{
		AttachStdout: true,
		AttachStderr: true,
		Cmd:          []string{"sh", "-c", command},
	}
	created, err := cli.ContainerExecCreate(ctx, containerID, execCfg)
	if err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	attach, err := cli.ContainerExecAttach(ctx, created.ID, container.ExecAttachOptions{})
	if err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	defer attach.Close()

	raw, err := io.ReadAll(attach.Reader)
	if err != nil {
		return ToolResult{Success: false, Output: "读取命令输出失败: " + err.Error()}
	}
	return ToolResult{Success: true, Output: t.truncate(demuxDockerLogs(raw))}
}

func (t *Toolset) dockerStart(ctx context.Context, params map[string]any) ToolResult {
	containerID, _ := params["container"].(string)
	containerID = strings.TrimSpace(containerID)
	if containerID == "" {
		return ToolResult{Success: false, Output: "container 不能为空"}
	}

	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	if err := cli.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	return ToolResult{Success: true, Output: "容器 " + containerID + " 已启动"}
}

func (t *Toolset) dockerStop(ctx context.Context, params map[string]any) ToolResult {
	containerID, _ := params["container"].(string)
	containerID = strings.TrimSpace(containerID)
	if containerID == "" {
		return ToolResult{Success: false, Output: "container 不能为空"}
	}
	timeoutSeconds := intParam(params, "timeout")

	cli, err := t.dockerClient()
	if err != nil {
		return ToolResult{Success: false, Output: err.Error()}
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(ctx, t.Timeout)
	defer cancel()
	var timeout *int
	if timeoutSeconds > 0 {
		timeout = &timeoutSeconds
	}
	if err := cli.ContainerStop(ctx, containerID, container.StopOptions{Timeout: timeout}); err != nil {
		return ToolResult{Success: false, Output: "Docker 操作失败: " + err.Error()}
	}
	return ToolResult{Success: true, Output: "容器 " + containerID + " 已停止"}
}

func dockerPorts(ports []container.Port) string {
	if len(ports) == 0 {
		return ""
	}
	var parts []string
	for _, p := range ports {
		host := ""
		if p.PublicPort > 0 {
			host = fmt.Sprintf("%d->", p.PublicPort)
		}
		parts = append(parts, fmt.Sprintf("%s%d/%s", host, p.PrivatePort, p.Type))
	}
	return strings.Join(parts, ", ")
}

func formatBytes(size int64) string {
	const unit = 1024
	if size < unit {
		return fmt.Sprintf("%dB", size)
	}
	div, exp := int64(unit), 0
	for n := size / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f%cB", float64(size)/float64(div), "KMGTPE"[exp])
}
