package config

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"frpc-tool/internal/util"
)

// Manager 负责配置文件的加载与部署。
type Manager struct {
	env    util.Environment
	client *http.Client
}

// NewManager 创建配置管理器。
func NewManager(env util.Environment) *Manager {
	return &Manager{
		env:    env,
		client: util.NewHTTPClient(30 * time.Second),
	}
}

// Deploy 写入最终配置文件，并返回配置路径。
func (m *Manager) Deploy(ctx context.Context, source string) (string, error) {
	data, err := m.Load(ctx, source)
	if err != nil {
		return "", err
	}

	if err := util.EnsureDir(m.env.FrpcDir, 0o755); err != nil {
		return "", fmt.Errorf("创建 frpc 目录失败: %w", err)
	}

	if _, err := util.BackupFile(m.env.ConfigPath); err != nil {
		return "", fmt.Errorf("备份旧配置失败: %w", err)
	}

	if err := util.AtomicWriteFile(m.env.ConfigPath, data, 0o644); err != nil {
		return "", fmt.Errorf("写入配置文件失败: %w", err)
	}
	return m.env.ConfigPath, nil
}

// Load 根据来源读取配置数据。
func (m *Manager) Load(ctx context.Context, source string) ([]byte, error) {
	switch {
	case source == "" || source == "default":
		return []byte(DefaultTemplate()), nil
	case util.IsHTTPURL(source):
		return m.loadFromURL(ctx, source)
	default:
		return m.loadFromFile(source)
	}
}

func (m *Manager) loadFromFile(path string) ([]byte, error) {
	if !filepath.IsAbs(path) {
		path = filepath.Join(m.env.BaseDir, path)
	}
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("读取配置文件失败: %w", err)
	}
	return data, nil
}

func (m *Manager) loadFromURL(ctx context.Context, url string) ([]byte, error) {
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "frpc-tool/1.0")

	resp, err := m.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("下载远程配置失败: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return nil, fmt.Errorf("下载远程配置失败: HTTP %d %s", resp.StatusCode, bytes.TrimSpace(body))
	}

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取远程配置失败: %w", err)
	}
	return data, nil
}
