package regionalserver

import (
	"context"
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/go-resty/resty/v2"
	clusterngv1beta "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"

	regionalserveradapter "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func (s *regionalServerCliImpl) ListLightweightBackups(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.ListLightweightBackupsRequest,
) (*clusterngv1beta.ListLightweightBackupsResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["ListLightweightBackups"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			request.SetQueryParam("from_prefix", req.GetFromPrefix())
			request.SetQueryParam("load_details", strconv.FormatBool(req.GetLoadDetails()))
			request.SetQueryParam("max_count", strconv.Itoa(int(req.GetMaxCount())))
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out clusterngv1beta.ListLightweightBackupsResponse
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) CreateLightweightRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.CreateLightweightRestoreRequest,
) bizerr.BizError {
	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	_, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["CreateLightweightRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return berr
	}

	return nil
}

func (s *regionalServerCliImpl) GetLightweightRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetLightweightRestoreRequest,
) (*clusterngv1beta.LightweightRestore, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetLightweightRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetPathParam(lightweightRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		if resp != nil && resp.StatusCode() == http.StatusNotFound {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "LightweightRestore not found")
		}
		return nil, err
	}

	var out clusterngv1beta.LightweightRestore
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) GetLightweightRestorePostActions(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetLightweightRestorePostActionsRequest,
) (*clusterngv1beta.GetLightweightRestorePostActionsResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetLightweightRestorePostActions"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetPathParam(lightweightRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out clusterngv1beta.GetLightweightRestorePostActionsResponse
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) DeleteLightweightRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.DeleteLightweightRestoreRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodDelete,
		operationMap["DeleteLightweightRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetPathParam(lightweightRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		return err
	}

	return nil
}

func (s *regionalServerCliImpl) CreateClassicRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.CreateClassicRestoreRequest,
) bizerr.BizError {
	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	_, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["CreateClassicRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return berr
	}

	return nil
}

func (s *regionalServerCliImpl) GetClassicRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetClassicRestoreRequest,
) (*clusterngv1beta.ClassicRestore, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetClassicRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetPathParam(classicRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		if resp != nil && resp.StatusCode() == http.StatusNotFound {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "ClassicRestore not found")
		}
		return nil, err
	}

	var out clusterngv1beta.ClassicRestore
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) DeleteClassicRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.DeleteClassicRestoreRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodDelete,
		operationMap["DeleteClassicRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetTargetSharedPoolId())
			request.SetPathParam(classicRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		return err
	}

	return nil
}

func (s *regionalServerCliImpl) TestClassicBackupAccess(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.TestClassicBackupAccessRequest,
) (*clusterngv1beta.TestClassicBackupAccessResponse, bizerr.BizError) {
	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["TestClassicBackupAccess"],
		func(request *resty.Request) *resty.Request {
			request.SetBody(body)
			return request
		},
	)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.GeneralInternalErrorCode, "failed to test classic backup access")
	}

	var out clusterngv1beta.TestClassicBackupAccessResponse
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}
	return &out, nil
}

func (s *regionalServerCliImpl) GetBackupGcSetting(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetBackupGcSettingRequest,
) (*clusterngv1beta.BackupGcSetting, bizerr.BizError) {
	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetBackupGcSetting"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetSharedPoolId())
			return request
		},
	)
	if berr != nil {
		return nil, berr
	}

	var out clusterngv1beta.BackupGcSetting
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}
	return &out, nil
}

func (s *regionalServerCliImpl) UpdateBackupGcSetting(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.UpdateBackupGcSettingRequest,
) (*clusterngv1beta.BackupGcSetting, bizerr.BizError) {
	body, err := marshalOptions.Marshal(req.BackupGcSetting)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	resp, berr := s.callRegion(
		ctx,
		region,
		http.MethodPut,
		operationMap["UpdateBackupGcSetting"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(sharedPoolIdPathParameter, req.GetBackupGcSetting().GetSharedPoolId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return nil, berr.Chain("failed to update backup gc setting")
	}

	var out clusterngv1beta.BackupGcSetting
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}
	return &out, nil
}

func (s *regionalServerCliImpl) UpdateAutoBackupMetering(
	ctx context.Context,
	region *entity.Region,
	req *regionalserveradapter.AutoBackupMeteringRequest,
) bizerr.BizError {
	body, err := json.Marshal(req)
	if err != nil {
		return bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	_, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["UpdateAutoBackupMetering"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.ClusterID)
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return berr.Chain("failed to update auto backup metetering")
	}
	return nil
}

func (s *regionalServerCliImpl) ReportAutoBackupMetering(
	ctx context.Context,
	region *entity.Region,
	req *regionalserveradapter.ReportAutoBackupMeteringRequest,
) bizerr.BizError {
	body, err := json.Marshal(req)
	if err != nil {
		return bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	_, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["ReportAutoBackupMetering"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.ClusterID)
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return berr.Chain("failed to report auto backup metering")
	}
	return nil
}

func (s *regionalServerCliImpl) CreateManualBackup(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.CreateManualBackupRequest,
) bizerr.BizError {
	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	_, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["CreateManualBackup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetClusterId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return berr
	}
	return nil
}

func (s *regionalServerCliImpl) GetManualBackup(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetManualBackupRequest,
) (*clusterngv1beta.ManualBackup, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetManualBackup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetClusterId())
			request.SetPathParam(manualBackupIdPathParameter, req.GetBackupId())
			return request
		},
	)
	if err != nil {
		if resp != nil && resp.StatusCode() == http.StatusNotFound {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "ManualBackup not found")
		}
		return nil, err
	}

	var out clusterngv1beta.ManualBackup
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) DeleteManualBackup(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.DeleteManualBackupRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodDelete,
		operationMap["DeleteManualBackup"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetClusterId())
			request.SetPathParam(manualBackupIdPathParameter, req.GetBackupId())
			return request
		},
	)
	if err != nil {
		return err
	}

	return nil
}

func (s *regionalServerCliImpl) CreateManualBackupRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.CreateManualBackupRestoreRequest,
) bizerr.BizError {
	body, err := marshalOptions.Marshal(req)
	if err != nil {
		return bizerr.New(bizerr.ErrCodeJsonMarshal, "%s", err.Error())
	}
	_, berr := s.callRegion(
		ctx,
		region,
		http.MethodPost,
		operationMap["CreateManualBackupRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetSourceClusterId())
			request.SetPathParam(manualBackupIdPathParameter, req.GetBackupId())
			request.SetBody(body)
			return request
		},
	)
	if berr != nil {
		return berr
	}
	return nil
}

func (s *regionalServerCliImpl) GetManualBackupRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetManualBackupRestoreRequest,
) (*clusterngv1beta.ManualBackupRestore, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetManualBackupRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetSourceClusterId())
			request.SetPathParam(manualBackupIdPathParameter, req.GetBackupId())
			request.SetPathParam(manualBackupRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		if resp != nil && resp.StatusCode() == http.StatusNotFound {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "ManualBackupRestore not found")
		}
		return nil, err
	}

	var out clusterngv1beta.ManualBackupRestore
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) GetManualBackupRestorePostActions(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.GetManualBackupRestorePostActionsRequest,
) (*clusterngv1beta.GetManualBackupRestorePostActionsResponse, bizerr.BizError) {
	resp, err := s.callRegion(
		ctx,
		region,
		http.MethodGet,
		operationMap["GetManualBackupRestorePostActions"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetSourceClusterId())
			request.SetPathParam(manualBackupIdPathParameter, req.GetBackupId())
			request.SetPathParam(manualBackupRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		return nil, err
	}

	var out clusterngv1beta.GetManualBackupRestorePostActionsResponse
	if err := unmarshalOptions.Unmarshal(resp.Body(), &out); err != nil {
		return nil, bizerr.New(bizerr.ErrCodeRegionalAPI, "%s", err.Error())
	}

	return &out, nil
}

func (s *regionalServerCliImpl) DeleteManualBackupRestore(
	ctx context.Context,
	region *entity.Region,
	req *clusterngv1beta.DeleteManualBackupRestoreRequest,
) bizerr.BizError {
	_, err := s.callRegion(
		ctx,
		region,
		http.MethodDelete,
		operationMap["DeleteManualBackupRestore"],
		func(request *resty.Request) *resty.Request {
			request.SetPathParam(clusterIdParameter, req.GetSourceClusterId())
			request.SetPathParam(manualBackupIdPathParameter, req.GetBackupId())
			request.SetPathParam(manualBackupRestoreIdPathParameter, req.GetRestoreId())
			return request
		},
	)
	if err != nil {
		return err
	}

	return nil
}
