package http

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

	"dataservices/commons/errs"
	"dataservices/internal/entity"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type EDAJobResponse struct {
	Code    int    `json:"code"`
	Message string `json:"msg"`
	Result  struct {
		JobID string `json:"job_id"`
	} `json:"result"`
}

type QueryJobResp struct {
	Code   int    `json:"code"`
	Msg    string `json:"msg"`
	Result struct {
		Status string `json:"status"`
	} `json:"result"`
}

type EDAChat2DataResp struct {
	Code   int    `json:"code"`
	Msg    string `json:"msg"`
	Result struct {
		EndedAt int    `json:"ended_at"`
		JobID   string `json:"job_id"`
		Reason  string `json:"reason"`
		Result  struct {
			ChartOptions struct {
				ChartName string `json:"chart_name"`
				Option    struct {
					Columns []string `json:"columns"`
				} `json:"option"`
				Title string `json:"title"`
			} `json:"chart_options"`
			ClarifiedTask string `json:"clarified_task"`
			Description   string `json:"description"`
			SQL           string `json:"sql"`
			Status        string `json:"status"`
			TaskID        string `json:"task_id"`
			Type          string `json:"type"`
		} `json:"result"`
		Status string `json:"status"`
	} `json:"result"`
}

func ProxyToEDAService(ctx context.Context, appInfo *entity.DataApp, req *http.Request, method string, timeout time.Duration) (string, int, error) {
	url := path.Join("/inner/eda" + req.URL.Path)
	cli := NewBasicAuthClient(WithTimeout(timeout))

	var (
		queryString = map[string]string{}
		header      = map[string]string{}
		payload     = map[string]interface{}{}
	)

	// forward query string
	for k, v := range req.URL.Query() {
		if len(v) > 0 {
			queryString[k] = v[0] // it's a restrict of tiinf, as the query string is a map[string]string
		}
	}
	// forward header
	for k, v := range req.Header {
		if len(v) > 0 {
			header[k] = v[0] // it's a restrict of tiinf, as the header is a map[string]string
		}
	}
	header["X-ORG-ID"] = fmt.Sprintf("%d", appInfo.OrgId)
	header["X-PROJECT-ID"] = fmt.Sprintf("%d", appInfo.ProjectId)
	header["X-App-ID"] = fmt.Sprintf("%d", appInfo.ID)
	header["X-External-App-ID"] = appInfo.ExternalAppID
	header["X-App-Type"] = fmt.Sprintf("%s", appInfo.AppType)
	header["X-Created-By"] = appInfo.CreatedBy

	// forward payload
	if req.Body != nil {
		data, err := io.ReadAll(req.Body)
		if err != nil {
			if err != io.EOF {
				tilog.Error(ctx, "failed to read request payload", zap.Error(err))
				return "", http.StatusBadRequest, err
			}
		} else if len(data) > 0 {
			if err = json.Unmarshal(data, &payload); err != nil {
				tilog.Error(ctx, "failed to read request payload", zap.Error(err), zap.String("data", string(data)))
				return "", http.StatusBadRequest, errs.NewBadRequest("invalid request payload, please check your payload is property json format", err)
			}
		}
	}

	tilog.Info(ctx, "request eda service", zap.String("method", method), zap.String("url", url), zap.Any("queryString", queryString), zap.Any("header", header))
	return cli.RequestGlobal(ctx, method, url, &queryString, &header, payload)
}

func QueryEDAJobDetail(ctx context.Context, appInfo *entity.DataApp, jobID string) (string, int, error) {
	url := fmt.Sprintf("/inner/eda/api/v1beta/app/%s/endpoint/v1/chatbot/jobs/%s", appInfo.ExternalAppID, jobID)
	cli := NewBasicAuthClient(WithTimeout(20 * time.Second))

	tilog.Info(ctx, "query eda job detail", zap.String("url", url))
	return cli.RequestGlobal(ctx, http.MethodGet, url, nil, nil, nil)
}

func LoopQueryEDAJob(ctx context.Context, appInfo *entity.DataApp, jobID string, maxRetries int) (string, int, error) {
	var (
		resp string
		code int
		err  error
	)

	for i := 0; i < maxRetries; i++ {
		resp, code, err = QueryEDAJobDetail(ctx, appInfo, jobID)
		if err != nil {
			tilog.Error(ctx, "query eda job detail failed", zap.Error(err), zap.String("jobID", jobID))
			return "", http.StatusInternalServerError, err
		}

		var queryJobResp QueryJobResp
		if err = json.Unmarshal([]byte(resp), &queryJobResp); err != nil {
			tilog.Error(ctx, "failed to unmarshal eda job detail response", zap.Error(err), zap.String("resp", resp))
			return "", http.StatusInternalServerError, err
		}

		if code == http.StatusOK && queryJobResp.Result.Status == "done" {
			return resp, code, nil
		}

		time.Sleep(time.Second)
	}

	return "", http.StatusRequestTimeout, errs.NewBadRequest("query eda job detail timeout", nil)
}
