package mcp

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/engigu/taskpool/internal/constant"
	"github.com/engigu/taskpool/internal/models/vo"
	"github.com/engigu/taskpool/internal/services"
)

var (
	openAPIClient     *Client
	openAPIClientOnce sync.Once
)

// GetOpenAPIClient 获取内部 OpenAPI 客户端（单例）
// 使用当前服务器的地址和系统设置中的 OpenAPI Token
func GetOpenAPIClient() *Client {
	openAPIClientOnce.Do(func() {
		cfg := services.GetConfig()
		baseURL := fmt.Sprintf("http://127.0.0.1:%d", cfg.Server.Port)
		if cfg.Server.URLPrefix != "" {
			baseURL += cfg.Server.URLPrefix
		}

		settingsSvc := services.NewSettingsService()
		token := getOpenAPIToken(settingsSvc)

		openAPIClient = NewClient(baseURL, token)
	})
	return openAPIClient
}

// RefreshClient 刷新客户端（当 OpenAPI Token 变更时调用）
func RefreshClient() {
	settingsSvc := services.NewSettingsService()
	token := getOpenAPIToken(settingsSvc)
	if openAPIClient != nil {
		openAPIClient.Token = token
	}
}

func getOpenAPIToken(settingsSvc *services.SettingsService) string {
	siteConfig := settingsSvc.GetSection(constant.SectionSite)
	tokenJson, ok := siteConfig[constant.KeyOpenapiToken]
	if !ok || tokenJson == "" {
		return ""
	}

	var tokenConfig vo.TokenConfig
	if err := json.Unmarshal([]byte(tokenJson), &tokenConfig); err != nil {
		return ""
	}

	if !tokenConfig.Enabled {
		return ""
	}

	return tokenConfig.Token
}

// Client 调用内部 OpenAPI
type Client struct {
	BaseURL    string
	Token      string
	HTTPClient *http.Client
}

func NewClient(baseURL, token string) *Client {
	return &Client{
		BaseURL: strings.TrimRight(strings.TrimSpace(baseURL), "/"),
		Token:   strings.TrimSpace(token),
		HTTPClient: &http.Client{
			Timeout: 60 * time.Second,
		},
	}
}

func (c *Client) openBase() string {
	return c.BaseURL + "/open2api/v1"
}

func (c *Client) do(method, path string, query map[string]string, body any) (json.RawMessage, error) {
	if c.BaseURL == "" {
		return nil, fmt.Errorf("服务器地址未配置")
	}
	if c.Token == "" {
		return nil, fmt.Errorf("OpenAPI Token 未配置，请在系统设置中启用 OpenAPI 并生成 Token")
	}

	u, err := url.Parse(c.openBase() + path)
	if err != nil {
		return nil, err
	}
	if len(query) > 0 {
		q := u.Query()
		for k, v := range query {
			if strings.TrimSpace(v) != "" {
				q.Set(k, v)
			}
		}
		u.RawQuery = q.Encode()
	}

	var reader io.Reader
	if body != nil {
		b, err := json.Marshal(body)
		if err != nil {
			return nil, err
		}
		reader = bytes.NewReader(b)
	}

	req, err := http.NewRequest(method, u.String(), reader)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+c.Token)
	req.Header.Set("Accept", "application/json")
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("请求失败: %w", err)
	}
	defer resp.Body.Close()

	raw, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var envelope struct {
		Code int             `json:"code"`
		Msg  string          `json:"msg"`
		Data json.RawMessage `json:"data"`
	}
	if err := json.Unmarshal(raw, &envelope); err != nil {
		if resp.StatusCode >= 400 {
			return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, truncate(string(raw), 500))
		}
		return raw, nil
	}

	if envelope.Code != 200 {
		msg := envelope.Msg
		if msg == "" {
			msg = truncate(string(raw), 500)
		}
		return nil, fmt.Errorf("[%d] %s", envelope.Code, msg)
	}
	if len(envelope.Data) > 0 && string(envelope.Data) != "null" {
		return envelope.Data, nil
	}
	result := map[string]any{"code": envelope.Code, "msg": envelope.Msg}
	b, _ := json.Marshal(result)
	return b, nil
}

func (c *Client) Get(path string, query map[string]string) (json.RawMessage, error) {
	return c.do(http.MethodGet, path, query, nil)
}

func (c *Client) Post(path string, body any) (json.RawMessage, error) {
	return c.do(http.MethodPost, path, nil, body)
}

func (c *Client) Put(path string, body any) (json.RawMessage, error) {
	return c.do(http.MethodPut, path, nil, body)
}

func (c *Client) Delete(path string) (json.RawMessage, error) {
	return c.do(http.MethodDelete, path, nil, nil)
}

func truncate(s string, n int) string {
	if len(s) <= n {
		return s
	}
	return s[:n] + "..."
}
