package service

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"3588AdminBackend/internal/config"
)

type AgentClient struct {
	cfg    *config.Config
	client *http.Client
	upload *http.Client
}

func NewAgentClient(cfg *config.Config) *AgentClient {
	return &AgentClient{
		cfg: cfg,
		client: &http.Client{
			Timeout: 3 * time.Second,
		},
		upload: &http.Client{Timeout: 120 * time.Second},
	}
}

func (c *AgentClient) Do(method, ip string, port int, path string, body []byte) ([]byte, int, error) {
	return c.DoStream(method, ip, port, path, bytes.NewReader(body), "", int64(len(body)))
}

func (c *AgentClient) DoStream(method, ip string, port int, path string, body io.Reader, contentType string, contentLength int64) ([]byte, int, error) {
	url := fmt.Sprintf("http://%s:%d%s", ip, port, path)
	var rc io.ReadCloser
	if body == nil {
		rc = http.NoBody
		contentLength = 0
	} else if r, ok := body.(io.ReadCloser); ok {
		rc = r
	} else {
		rc = io.NopCloser(body)
	}

	ctx := context.Background()
	req, err := http.NewRequestWithContext(ctx, method, url, rc)
	if err != nil {
		return nil, 0, err
	}
	if contentLength >= 0 {
		req.ContentLength = contentLength
	}

	if c.cfg.AgentToken != "" {
		req.Header.Set("X-RK-Token", c.cfg.AgentToken)
	}

	if req.Body != nil && req.Body != http.NoBody {
		ct := strings.TrimSpace(contentType)
		if ct == "" {
			ct = defaultContentTypeForPath(path)
		}
		if ct != "" {
			req.Header.Set("Content-Type", ct)
		}
	}

	client := c.client
	if isLongOp(method, path) {
		client = c.upload
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	defer resp.Body.Close()

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

	return respBody, resp.StatusCode, nil
}

func defaultContentTypeForPath(path string) string {
	switch {
	case strings.HasPrefix(path, "/v1/models/"):
		return "application/octet-stream"
	case strings.HasPrefix(path, "/v1/resources/"):
		return "application/octet-stream"
	case path == "/v1/face-gallery":
		return "application/octet-stream"
	default:
		return "application/json"
	}
}

func isLongOp(method, path string) bool {
	if method == http.MethodPut && path == "/v1/config" {
		return true
	}
	if method == http.MethodPost && path == "/v1/config/candidate/apply" {
		return true
	}
	if method == http.MethodPost && path == "/v1/media-server/rollback" {
		return true
	}
	if strings.HasPrefix(path, "/v1/config/ui/") {
		return true
	}
	if strings.HasPrefix(path, "/v1/models/") {
		return true
	}
	if strings.HasPrefix(path, "/v1/resources/") {
		return true
	}
	if path == "/v1/face-gallery" {
		return true
	}
	return false
}
