package task_manager

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

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/api/common"
	"github.com/pingcap-inc/clinic/pkg/config"
)

const (
	rebuild          = "/task_manager/api/v1/rebuild"
	status           = "/task_manager/api/v1/%s/status"
	reportStatus     = "/task_manager/api/v1/%s/reportStatus"
	comparisonReport = "/task_manager/api/v1/comparisonReport"
)

// for http client reuse

type TaskManagerClient struct {
	client *http.Client
	path   string
}

func NewTaskManagerClient(addr config.TaskManagerConfig) *TaskManagerClient {
	client := &http.Client{}
	return &TaskManagerClient{
		client: client,
		path:   fmt.Sprintf("%s:%d", addr.Host, addr.Port),
	}
}

func (tmc *TaskManagerClient) Rebuild(val common.Rebuild) error {
	data, err := json.Marshal(val)
	if err != nil {
		return err
	}
	req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("http://%s%s", tmc.path, rebuild), bytes.NewBuffer(data))
	if err != nil {
		log.Error("request failed", zap.Error(err))
		return err
	}
	resp, err := tmc.client.Do(req)
	if err != nil {
		log.Error("request task manager failed", zap.Error(err))
		return err
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusAccepted {
		bs, err := io.ReadAll(resp.Body)
		log.Error("rebuild failed", zap.String("error msg", string(bs)), zap.Error(err))
		return fmt.Errorf("rebuild task failed, got status %s", resp.Status)
	}

	return nil
}

func (tmc *TaskManagerClient) Status(fileuuid string) ([]byte, error) {
	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s%s", tmc.path, fmt.Sprintf(status, fileuuid)), nil)
	if err != nil {
		log.Error("request failed", zap.Error(err))
		return nil, err
	}
	resp, err := tmc.client.Do(req)
	if err != nil {
		log.Error("request task manager failed", zap.Error(err))
		return nil, err
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	// body := []byte{}
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		log.Error("rebuild failed", zap.String("error msg", string(body)), zap.Error(err))
		return nil, err
	}
	return body, nil
}

func (tmc *TaskManagerClient) ReportStatus(fileuuid string) (*common.TaskStatusResp, error) {
	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s%s", tmc.path, fmt.Sprintf(reportStatus, fileuuid)), nil)
	if err != nil {
		log.Error("request failed", zap.Error(err))
		return nil, err
	}
	resp, err := tmc.client.Do(req)
	if err != nil {
		log.Error("request task manager failed", zap.Error(err))
		return nil, err
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		log.Error("rebuild failed", zap.String("error msg", string(body)), zap.Error(err))
		return nil, err
	}
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error("read response failed", zap.Error(err))
		return nil, err
	}
	var val common.TaskStatusResp
	if err := json.Unmarshal(data, &val); err != nil {
		log.Error("unmarshal failed", zap.Error(err))
		return nil, err
	}
	return &val, nil
}

func (tmc *TaskManagerClient) TouchComparisonReport(param common.Comparison) (*common.TaskReponse, error) {
	data, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("http://%s%s", tmc.path, comparisonReport), bytes.NewBuffer(data))
	if err != nil {
		log.Error("request failed", zap.Error(err))
		return nil, err
	}
	resp, err := tmc.client.Do(req)
	if err != nil {
		log.Error("request task manager failed", zap.Error(err))
		return nil, err
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusAccepted {
		body, _ := io.ReadAll(resp.Body)
		log.Error("rebuild failed", zap.String("error msg", string(body)), zap.Error(err))
		return nil, errors.New(string(body))
	}
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error("read response failed", zap.Error(err))
		return nil, err
	}
	log.Info("body", zap.Any("body", body))
	var tresp common.TaskReponse
	if err := json.Unmarshal(body, &tresp); err != nil {
		log.Error("unmarshal failed", zap.Error(err))
		return nil, err
	}
	log.Info("resp", zap.Any("tresp", tresp))
	return &tresp, nil
}
