package plugin

import (
	"ALLinSSL/backend/public"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os/exec"
	"path/filepath"
)

var (
	ErrPluginNotFound = errors.New("插件未找到")
	ErrActionNotFound = errors.New("插件不支持该 action")
	pluginRegistry    = map[string]PluginMetadata{}
)

type ActionInfo struct {
	Name        string         `json:"name"`
	Description string         `json:"description"`
	Params      map[string]any `json:"params,omitempty"` // 可选参数
}

type PluginMetadata struct {
	Name        string         `json:"name"`
	Description string         `json:"description"`
	Version     string         `json:"version"`
	Author      string         `json:"author"`
	Actions     []ActionInfo   `json:"actions"`
	Config      map[string]any `json:"config,omitempty"` // 可选配置
	Path        string         // 插件路径
}

type Request struct {
	Action string                 `json:"action"`
	Params map[string]interface{} `json:"params,omitempty"`
}

type Response struct {
	Status  string                 `json:"status"`
	Message string                 `json:"message"`
	Result  map[string]interface{} `json:"result"`
}

func scanPlugins(dir string) ([]PluginMetadata, error) {
	pluginRegistry = map[string]PluginMetadata{} // 清空旧的
	var plugins []PluginMetadata
	_ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
		if err != nil || d.IsDir() {
			return nil
		}
		meta, err := getMetadata(path)
		if err != nil {
			fmt.Println("插件无效:", path, "错误:", err)
			return nil
		}
		meta.Path = path
		plugins = append(plugins, meta)
		pluginRegistry[meta.Name] = meta
		return nil
	})
	return plugins, nil
}

func getMetadata(path string) (PluginMetadata, error) {
	req := Request{Action: "get_metadata"}
	data, _ := json.Marshal(req)

	cmd := exec.Command(path)
	cmd.Stdin = bytes.NewReader(data)
	var out bytes.Buffer
	cmd.Stdout = &out

	if err := cmd.Run(); err != nil {
		return PluginMetadata{}, fmt.Errorf("运行失败: %w", err)
	}

	var resp Response
	if err := json.Unmarshal(out.Bytes(), &resp); err != nil {
		return PluginMetadata{}, fmt.Errorf("输出无效: %w", err)
	}
	if resp.Status != "success" {
		return PluginMetadata{}, fmt.Errorf("插件响应错误: %s", resp.Message)
	}

	var meta PluginMetadata
	raw, _ := json.Marshal(resp.Result)
	if err := json.Unmarshal(raw, &meta); err != nil {
		return PluginMetadata{}, fmt.Errorf("元数据解析失败: %w", err)
	}

	if meta.Name == "" || len(meta.Actions) == 0 {
		return PluginMetadata{}, fmt.Errorf("元数据缺失")
	}

	return meta, nil
}

func CallPlugin(name, action string, params map[string]interface{}, logger *public.Logger) (*Response, error) {
	// 第一次尝试
	resp, err := tryCallPlugin(name, action, params, logger)
	if err == nil {
		return resp, nil
	}

	// 如果是插件或 action 不存在，则刷新插件列表并再试一次
	if errors.Is(err, ErrPluginNotFound) || errors.Is(err, ErrActionNotFound) {
		logger.Debug("插件或插件内方法不存在，尝试刷新插件列表...")
		_, scanErr := GetPlugins()
		if scanErr != nil {
			logger.Error("插件刷新失败", scanErr)
			return nil, fmt.Errorf("插件刷新失败: %v", scanErr)
		}
		return tryCallPlugin(name, action, params, logger)
	}

	// 其他错误直接返回
	return nil, err
}

func tryCallPlugin(name, action string, params map[string]interface{}, logger *public.Logger) (*Response, error) {
	plugin, ok := pluginRegistry[name]
	if !ok {
		return nil, ErrPluginNotFound
	}

	// 检查 action 是否存在
	found := false
	for _, a := range plugin.Actions {
		if a.Name == action {
			found = true
			break
		}
	}
	if !found {
		logger.Debug("插件不支持该 action", "plugin", name, "action", action)
		return nil, ErrActionNotFound
	}

	// 构造请求
	req := Request{
		Action: action,
		Params: params,
	}

	// 启动插件进程
	cmd := exec.Command(plugin.Path)
	cmd.Stderr = io.Discard // ❌ 忽略所有插件错误/日志输出，避免污染

	stdin, err := cmd.StdinPipe()
	if err != nil {
		logger.Error("开启标准输入管道失败", err)
		return nil, err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		logger.Error("开启标准输出管道失败", err)
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		logger.Error("启动插件失败", err)
		return nil, err
	}

	if err := json.NewEncoder(stdin).Encode(req); err != nil {
		logger.Error("发送插件请求失败", err)
		return nil, err
	}
	stdin.Close()

	respBytes, err := io.ReadAll(stdout)
	if err != nil {
		logger.Error("读取插件响应失败", err)
		return nil, err
	}
	var resp Response
	if err := json.Unmarshal(respBytes, &resp); err != nil {
		logger.Error("解析插件响应失败", err, "内容", string(respBytes))
		return nil, fmt.Errorf("解析插件响应失败: %v\n内容: %s", err, respBytes)
	}
	cmd.Wait()
	logger.Debug("插件响应", "plugin", name, "action", action, "response", resp)
	if resp.Status != "success" {
		return nil, fmt.Errorf("插件响应错误: %s", resp.Message)
	}

	return &resp, nil
}

func GetPlugins() ([]PluginMetadata, error) {
	pluginDir := public.GetSettingIgnoreError("plugin_dir")
	return scanPlugins(pluginDir)
}

func GetActions(pluginName string) ([]ActionInfo, error) {
	_, err := GetPlugins()
	if err != nil {
		return nil, fmt.Errorf("获取插件列表失败: %v", err)
	}
	return pluginRegistry[pluginName].Actions, err
}
