package client

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/tidbcloud/observability/agent-control-server/pkg/agentcontrol/api"
)

const (
	agentControlEndpoint = "o11y-agent-control-server:8086"
	timeout              = 10 * time.Second
)

var _ api.AgentControlAPI = &AgentControlClient{}

type AgentControlClient struct {
	endpoint string
	client   *http.Client
}

func NewAgentControlClient() *AgentControlClient {
	return &AgentControlClient{
		endpoint: agentControlEndpoint,
		client: &http.Client{
			Timeout: timeout,
		},
	}
}

func (a *AgentControlClient) CreateK8sAgent(ctx context.Context, req *api.CreateK8sAgentRequest) (*api.CreateK8sAgentResponse, error) {
	payload, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal json: %w", err)
	}
	resp, err := a.request(ctx, http.MethodPost, fmt.Sprintf("%s/api/v1/k8sagents/", a.endpoint), payload)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if (resp.StatusCode < 200 || resp.StatusCode > 299) && resp.StatusCode != http.StatusConflict {
		return nil, fmt.Errorf("failed to create k8s agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.CreateK8sAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) DeleteK8sAgent(ctx context.Context, req *api.DeleteK8sAgentRequest) (*api.DeleteK8sAgentResponse, error) {
	// u, err := url.Parse(fmt.Sprintf("%s/api/v1/k8sagents/%s", a.endpoint, req.ID))
	// if err != nil {
	// 	return nil, err
	// }
	// q := u.Query()
	// q.Set("vendor", req.Vendor)
	// q.Set("region", req.Region)
	// u.RawQuery = q.Encode()
	resp, err := a.request(ctx, http.MethodDelete, fmt.Sprintf("%s/api/v1/k8sagents/%s", a.endpoint, req.ProjectID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if (resp.StatusCode < 200 || resp.StatusCode > 299) && resp.StatusCode != http.StatusNotFound {
		return nil, fmt.Errorf("failed to delete k8s agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.DeleteK8sAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) ListK8sAgents(ctx context.Context, req *api.ListK8sAgentsRequest) (*api.ListK8sAgentsResponse, error) {
	resp, err := a.request(ctx, http.MethodGet, fmt.Sprintf("%s/api/v1/k8sagents/", a.endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to list k8s agents, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.ListK8sAgentsResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) GetK8sAgent(ctx context.Context, req *api.GetK8sAgentRequest) (*api.GetK8sAgentResponse, error) {
	resp, err := a.request(ctx, http.MethodGet, fmt.Sprintf("%s/api/v1/k8sagents/%s", a.endpoint, req.ProjectID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode == http.StatusNotFound {
		return nil, api.ErrNotFound
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to get k8s agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.GetK8sAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) CreateTiDBAgent(ctx context.Context, req *api.CreateTiDBAgentRequest) (*api.CreateTiDBAgentResponse, error) {
	payload, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal json: %w", err)
	}
	resp, err := a.request(ctx, http.MethodPost, fmt.Sprintf("%s/api/v1/tidbagents/", a.endpoint), payload)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if (resp.StatusCode < 200 || resp.StatusCode > 299) && resp.StatusCode != http.StatusConflict {
		return nil, fmt.Errorf("failed to create tidb agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.CreateTiDBAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) DeleteTiDBAgent(ctx context.Context, req *api.DeleteTiDBAgentRequest) (*api.DeleteTiDBAgentResponse, error) {
	resp, err := a.request(ctx, http.MethodDelete, fmt.Sprintf("%s/api/v1/tidbagents/%s", a.endpoint, req.ClusterID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if (resp.StatusCode < 200 || resp.StatusCode > 299) && resp.StatusCode != http.StatusNotFound {
		return nil, fmt.Errorf("failed to delete tidb agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.DeleteTiDBAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) ListTiDBAgents(ctx context.Context, req *api.ListTiDBAgentsRequest) (*api.ListTiDBAgentsResponse, error) {
	resp, err := a.request(ctx, http.MethodGet, fmt.Sprintf("%s/api/v1/tidbagents/", a.endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to list tidb agents, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.ListTiDBAgentsResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) GetTiDBAgent(ctx context.Context, req *api.GetTiDBAgentRequest) (*api.GetTiDBAgentResponse, error) {
	resp, err := a.request(ctx, http.MethodGet, fmt.Sprintf("%s/api/v1/tidbagents/%s", a.endpoint, req.ClusterID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode == http.StatusNotFound {
		return nil, nil
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to get tidb agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.GetTiDBAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) PauseTiDBAgent(ctx context.Context, req *api.PauseTiDBAgentRequest) (*api.PauseTiDBAgentResponse, error) {
	resp, err := a.request(ctx, http.MethodPut, fmt.Sprintf("%s/api/v1/tidbagents/%s/pause", a.endpoint, req.ClusterID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to pause tidb agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.PauseTiDBAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) ResumeTiDBAgent(ctx context.Context, req *api.ResumeTiDBAgentRequest) (*api.ResumeTiDBAgentResponse, error) {
	resp, err := a.request(ctx, http.MethodPut, fmt.Sprintf("%s/api/v1/tidbagents/%s/resume", a.endpoint, req.ClusterID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to resume tidb agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.ResumeTiDBAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) ScaleTiDBAgent(ctx context.Context, req *api.ScaleTiDBAgentRequest) (*api.ScaleTiDBAgentResponse, error) {
	resp, err := a.request(ctx, http.MethodPut, fmt.Sprintf("%s/api/v1/tidbagents/%s/scale", a.endpoint, req.ClusterID), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to do request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("failed to scale tidb agent, status code: %d, body: %s", resp.StatusCode, string(body))
	}
	var r api.ScaleTiDBAgentResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("failed to unmarshal json: %v, body: %s", err, string(body))
	}
	return &r, nil
}

func (a *AgentControlClient) request(ctx context.Context, method, url string, body []byte) (*http.Response, error) {
	var req *http.Request
	var err error
	if len(body) > 0 {
		if req, err = http.NewRequestWithContext(ctx, method, url, bytes.NewReader(body)); err != nil {
			return nil, err
		}
		req.Header.Add("Content-Type", "application/json")
	} else {
		if req, err = http.NewRequestWithContext(ctx, method, url, nil); err != nil {
			return nil, err
		}
	}
	return a.client.Do(req)
}
