package api

import (
	"bytes"
	"encoding/csv"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/pingcap/dbaas/billing2/utils/labels"

	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/auditloghandler"
	"github.com/pingcap/dbaas/billing2/handler/cachehandler"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/semaphore"
	"github.com/tidbcloud/tiinf/sdk/auditlog"
	"go.uber.org/zap"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/constant"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"github.com/pingcap/dbaas/billing2/model/costexplorer"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
)

type costExplorerOpenAPI struct {
}

type OpenapiMonthlyCostURI struct {
	//TenantID    uint   `uri:"tenant_id" binding:"required"`
	BilledMonth string `uri:"billedMonth" binding:"required"`
}
type OpenapiMonthlyClusterCostURI struct {
	ClusterID   uint   `uri:"cluster_id" binding:"required"`
	BilledMonth string `uri:"billedMonth" binding:"required"`
}

func listAliCustomerTenantIDsAndBillingConfigIDs(managerID uint) ([]uint, []uint, error) {
	if managerID != config.Get().GetUint(config.AlicloudManagingOrgID) {
		return nil, nil, errors.New("managing organization is not allowed for this organization")
	}
	aliCustomerBillingConfigs, err := (&billingconfig.BillingConfigModel{}).ListAlicloudCustomerBillingConfigs()
	if err != nil {
		return nil, nil, err
	}
	tenantsIDs := make([]uint, len(aliCustomerBillingConfigs))
	billingConfigIDs := make([]uint, len(aliCustomerBillingConfigs))
	for i, b := range aliCustomerBillingConfigs {
		tenantsIDs[i] = b.TenantID
		billingConfigIDs[i] = b.BillingConfigID
	}
	if len(tenantsIDs) == 0 {
		return []uint{0}, []uint{0}, nil // ensure do not generate empty tenant query
	}
	return tenantsIDs, billingConfigIDs, nil
}

// @Tags Billing
// @Summary Return organization monthly bills
// @Description  This endpoint retrieves the bill for the given month of a specific organization. You can query a bill up to 6 months prior to the current month. The bill is returned in JSON format.To use this resource, the requesting API Key must have the Organization Billing Admin or Organization Owner role.
// @Schemes
// @Accept json
// @Produce json
// @Param billedMonth path string true "The month of this bill happens for the specified organization, format is YYYY-MM, for example '2023-08'."
// @Success 200 {object} forms.BillsExplorer
// @Failure 400  {object}  billing_errors.BaseError
// @Router /bills/{billedMonth} [GET]
func (s *costExplorerOpenAPI) BillsExplorerOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	log.Info("/bills/{billedMonth} BillsExplorerOpenAPI ", zap.Uint("tenantID", billingCfgStruct.TenantID), zap.String("billedMonth", query.BilledMonth))

	if err != nil {
		return nil, err
	}
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSPCustomer:
		return nil, billing_errors.ForbiddenForPartnerManagedCustomersError
	}
	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	now := timeutils.GetNow()
	startMonth := timeutils.GetMonthDate(now).AddDate(0, constant.QueryBillsMonthLimit, 0)
	if billedMonth.Before(startMonth) || billedMonth.After(now) {
		return nil, billing_errors.NewInvalidInputError(errors.New("Param billedMonth is invalid. You can query a bill up to 6 months prior to the current month."))
	}
	model := &costexplorer.CostExplorerModel{}
	return model.CalculateOpenAPIMonthlyCostWithCache(billingCfgStruct.TenantID, billingCfgStruct.BillingConfigID, billedMonth)

}

func (s *costExplorerOpenAPI) ClusterBillsExplorerOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyClusterCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	log.Info("/bills/{billedMonth}/clusters/{cluster_id} ClusterBillsExplorerOpenAPI ", zap.Uint("tenantID", billingCfgStruct.TenantID), zap.String("billedMonth", query.BilledMonth), zap.Uint("clusterID", query.ClusterID))

	if err != nil {
		return nil, err
	}
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}

	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MarketplaceResellerManagedCustomer, entity.BillingConfigType_MSPCustomer:
		return nil, billing_errors.ForbiddenForPartnerManagedCustomersError
	}
	_, err = labels.GetClusterWithCache(billingCfgStruct.TenantID, query.ClusterID)
	if err != nil {
		if errors.Is(err, billing_errors.ObjectNotExistsError) {
			return nil, billing_errors.NewInvalidInputError(errors.New("ClusterBillsExplorerOpenAPI ClusterID Param is invalid"))
		}
		return nil, billing_errors.NewSystemError(err)
	}

	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	now := timeutils.GetNow()
	startMonth := timeutils.GetMonthDate(now).AddDate(0, constant.QueryBillsMonthLimit, 0)
	if billedMonth.Before(startMonth) || billedMonth.After(now) {
		return nil, billing_errors.NewInvalidInputError(errors.New("Param billedMonth is invalid. You can query a bill up to 6 months prior to the current month."))
	}
	model := &costexplorer.CostExplorerModel{}
	return model.CalculateOpenAPIMonthlyClusterCost(billingCfgStruct.TenantID, billingCfgStruct.BillingConfigID, billedMonth, query.ClusterID)

}

// @Tags Billing
// @Summary Get the monthly bill of the organization with daily usage details
// @Description This endpoint retrieves the bill for the given month of a specific organization with daily usage details. You can query a bill up to 6 months prior to the current month. The bill is returned in JSON format. To use this resource, the requesting API Key must have the Organization Billing Admin or Organization Owner role.
// @Schemes
// @Accept json
// @Produce json
// @Param billedMonth path string true "The month of this bill happens for the specified organization. The format is `YYYY-MM`, for example '2024-05'."
// @Success 200 {object} forms.BillsDetails
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsDetails/{billedMonth} [GET]
func (s *costExplorerOpenAPI) BillsDetailsOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	log.Info("/billsDetails/{billedMonth} BillsDetailsOpenAPI ", zap.Uint("tenantID", billingCfgStruct.TenantID), zap.String("billedMonth", query.BilledMonth))

	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSPCustomer:
		return nil, billing_errors.ForbiddenForPartnerManagedCustomersError
	}
	billingConfigID := billingCfgStruct.BillingConfigID
	tenantID := billingCfgStruct.TenantID
	defer func() {
		auditloghandler.Log(auditlog.EventShowBill, c, tenantID, nil, err)
	}()
	model := &costexplorer.CostExplorerModel{}
	costs, err := model.GetBillsDetailsWithCache(tenantID, billingConfigID, billedMonth.Year(), int(billedMonth.Month()))
	if err != nil {
		return nil, err
	}
	ret := forms.BillsDetails{}
	for _, cost := range costs.Details {
		ret.BillsDetails = append(ret.BillsDetails, forms.BillsDetail{
			BilledDate:      cost.BilledDate,
			ProjectName:     cost.ProjectName,
			ClusterName:     cost.ClusterName,
			ServicePathName: cost.Service,
			BillsCosts: forms.BillsCosts{
				RunningTotal: cost.Amount,
				Discounts:    cost.DiscountCost,
				Credits:      cost.CreditUsage,
				TotalCost:    cost.BillableCost,
			},
		})

	}

	return ret, nil
}

func (s *costExplorerOpenAPI) ClusterBillsDetailsOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyClusterCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	log.Info("/billsDetails/{billedMonth} ClusterBillsDetailsOpenAPI ", zap.Uint("tenantID", billingCfgStruct.TenantID), zap.String("billedMonth", query.BilledMonth), zap.Uint("clusterID", query.ClusterID))

	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MarketplaceResellerManagedCustomer, entity.BillingConfigType_MSPCustomer:
		return nil, billing_errors.ForbiddenForPartnerManagedCustomersError
	}
	billingConfigID := billingCfgStruct.BillingConfigID
	tenantID := billingCfgStruct.TenantID
	_, err = labels.GetClusterWithCache(tenantID, query.ClusterID)
	if err != nil {
		if errors.Is(err, billing_errors.ObjectNotExistsError) {
			return nil, billing_errors.NewInvalidInputError(errors.New("ClusterBillsDetailsOpenAPI ClusterID Param is invalid"))
		}
		return nil, billing_errors.NewSystemError(err)
	}

	defer func() {
		auditloghandler.Log(auditlog.EventShowBill, c, tenantID, nil, err)
	}()
	model := &costexplorer.CostExplorerModel{}
	costs, err := model.GetClusterBillsDetailsWithCache(tenantID, billingConfigID, billedMonth.Year(), int(billedMonth.Month()), query.ClusterID)
	if err != nil {
		return nil, err
	}
	ret := forms.BillsDetails{}
	for _, cost := range costs.Details {
		ret.BillsDetails = append(ret.BillsDetails, forms.BillsDetail{
			BilledDate:      cost.BilledDate,
			ProjectName:     cost.ProjectName,
			ClusterName:     cost.ClusterName,
			ServicePathName: cost.Service,
			BillsCosts: forms.BillsCosts{
				RunningTotal: cost.Amount,
				Discounts:    cost.DiscountCost,
				Credits:      cost.CreditUsage,
				TotalCost:    cost.BillableCost,
			},
		})

	}

	return ret, nil
}

// @Tags Billing
// @Summary Return basic info of a specific organization, which contains the key info to execute a cost explorer query
// @Description This endpoint retrieves the basic info for a specific organization, including service info, cloud provider info, project info, and cluster info.
// @Schemes
// @Accept json
// @Produce json
// @Success 200 {object} forms.TrendsArgs
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsCostExplorer/args [Get]
func (s *costExplorerOpenAPI) TrendsArgsOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	tenantID := billingCfgStruct.TenantID
	log.Info("/billsDetails/{billedMonth} BillsDetailsOpenAPI ", zap.Uint("tenantID", billingCfgStruct.TenantID))

	funcName := "TrendsArgsOpenAPI"
	var ret forms.TrendsArgs
	if cachehandler.Get().Get(funcName, &ret, tenantID) {
		return ret, nil
	}
	clusters, err := costexplorer.GetCostExplorerClusters(tenantID)
	if err != nil {
		return nil, err
	}
	projects, err := costexplorer.GetCostExplorerProjects(tenantID)
	if err != nil {
		return nil, err
	}
	providers, err := costexplorer.GetCostExplorerProviders()
	if err != nil {
		return nil, err
	}
	services, err := costexplorer.GetCostExplorerServices()
	if err != nil {
		return nil, err
	}
	productsSet := make(map[string]*struct{})
	for _, svc := range services {
		productsSet[*svc.Product] = nil
	}
	var clustersOpenAPI []*forms.ClusterOpenAPI
	for _, cluster := range clusters {
		clustersOpenAPI = append(clustersOpenAPI, &forms.ClusterOpenAPI{
			ID:         cluster.ID,
			ProjectID:  cluster.ProjectID,
			RegionName: cluster.RegionName,
			Name:       cluster.Name,
			Status:     cluster.Status,
		})
	}
	var projectsOpenAPI []*forms.ProjectOpenAPI
	for _, project := range projects {
		projectsOpenAPI = append(projectsOpenAPI, &forms.ProjectOpenAPI{
			ID:     project.ID,
			Name:   project.Name,
			Status: project.Status,
		})
	}
	ret = forms.TrendsArgs{
		Services:  services,
		Providers: providers,
		Clusters:  clustersOpenAPI,
		Projects:  projectsOpenAPI,
	}
	cachehandler.Get().Set(funcName, &ret, tenantID)
	return &ret, nil
}

// @Tags Billing
// @Summary Get the usage details according to a cost explorer query
// @Description This endpoint executes a cost explorer query and returns the usage details. Cost Explorer query:
// 1. Allow users to group costs by category
// 2. Allow users to focus on a specific cost item by filter
// 3. Allow users to query cost in the specific time range
// @Schemes
// @Accept json
// @Produce json
// @Param args body forms.TrendsInput true "args"
// @Success 200 {object} forms.BillsCostExplorer
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsCostExplorer [POST]
func (s *costExplorerOpenAPI) TrendsOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	log.Info("/billsCostExplorer", zap.Uint("tenantID", billingCfgStruct.TenantID))

	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MarketplaceResellerManagedCustomer, entity.BillingConfigType_MSPCustomer:
		return nil, billing_errors.ForbiddenForPartnerManagedCustomersError
	}
	tenantID, billingConfigID := billingCfgStruct.TenantID, billingCfgStruct.BillingConfigID
	query, err := parseTrendsQuery(c, tenantID, true)
	if err != nil {
		return nil, err
	}
	var ret *forms.CostExploreTrendsOutput
	timeSpan := int(query.EndDate.Sub(*query.StartDate).Hours()) / 24
	if timeSpan > config.Get().GetInt(config.CostExplorerTrendsLongTimeSpanQueryThresholdDays) {
		semaphore.ExecutionWithSemaphore(costExploreTrendsLongTimeSpanSem, func() {
			ret, err = costexplorer.CostTrends(tenantID, billingConfigID, query)
		})
	} else {
		ret, err = costexplorer.CostTrends(tenantID, billingConfigID, query)
	}
	if err != nil {
		return nil, err
	}

	return copyBillsCostExplorerFromCostExploreTrendsOutput(ret)
}

// @Id Billing_GetPartnerBills
// @Tags Partner, PartnerBilling
// @Summary Get organization monthly bills
// @Description  This endpoint retrieves the bill for the given month of a specific organization. You can query a bill up to 6 months prior to the current month. The bill is returned in JSON format.To use this resource, the requesting API Key must have the Organization Billing Admin or Organization Owner role.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param billedMonth path string true "The month for which to retrieve the bill, in the format of `YYYY-MM`. For example, `2024-08`."
// @Success 200 {object} forms.PartnerBillsExplorer
// @Failure 400  {object}  billing_errors.BaseError
// @Router /bills/{billedMonth} [GET]
func (s *costExplorerOpenAPI) GetPartnerBills(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	now := timeutils.GetNow()
	startMonth := timeutils.GetMonthDate(now).AddDate(0, constant.QueryBillsMonthLimit, 0)
	if billedMonth.Before(startMonth) || billedMonth.After(now) {
		return nil, billing_errors.NewInvalidInputError(errors.New("Param billedMonth is invalid. You can query a bill up to 6 months prior to the current month."))
	}
	model := &costexplorer.CostExplorerModel{}
	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSP, entity.BillingConfigType_ResalePartner:
		return model.CalculatePartnerOpenAPIMonthlyCostWithCache([]uint{billingCfgStruct.TenantID}, []uint{billingCfgStruct.BillingConfigID}, billedMonth, false)
	case entity.BillingConfigType_MarketplaceReseller, entity.BillingConfigType_DirectCustomer:
		log.Info("need to retrieve costs from managing billing configs", zap.String("billingConfigType", billingCfgStruct.Type), zap.Uint("tenantID", billingCfgStruct.TenantID))
		billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetManagingBillingConfigs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, err
		}
		if len(billingConfigs) == 0 {
			log.Info("no managing billing configs", zap.Uint("tenantID", billingCfgStruct.TenantID))
			return nil, nil
		}
		billingConfigIDs := make([]uint, len(billingConfigs))
		for i, b := range billingConfigs {
			billingConfigIDs[i] = b.BillingConfigID
		}
		return model.CalculatePartnerOpenAPIMonthlyCostWithCache([]uint{billingCfgStruct.TenantID}, billingConfigIDs, billedMonth, false)
	case entity.BillingConfigType_Alicloud:
		tenantIDs, billingConfigIDs, err := listAliCustomerTenantIDsAndBillingConfigIDs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, err
		}
		return model.CalculatePartnerOpenAPIMonthlyCostWithCache(tenantIDs, billingConfigIDs, billedMonth, true)
	default:
		return nil, billing_errors.ObjectNotExistsError
	}
}

// @Id Billing_GetPartnerBillsDetails
// @Tags Partner, PartnerBilling
// @Summary Get the monthly bill of the organization with daily usage details
// @Description This endpoint retrieves the bill for the given month of a specific organization with daily usage details. You can query a bill up to 6 months prior to the current month. The bill is returned in JSON format. To use this resource, the requesting API Key must have the Organization Billing Admin or Organization Owner role.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param billedMonth path string true "The month of this bill happens for the specified organization, in the format of `YYYY-MM`. For example `2024-08`."
// @Success 200 {object} forms.PartnerBillsDetails
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsDetails/{billedMonth} [GET]
func (s *costExplorerOpenAPI) GetPartnerBillsDetails(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	tenantID := billingCfgStruct.TenantID
	defer func() {
		auditloghandler.Log(auditlog.EventShowBill, c, tenantID, nil, err)
	}()
	model := &costexplorer.CostExplorerModel{}
	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSP, entity.BillingConfigType_ResalePartner:
		return model.GetPartnerBillsDetailsWithCache([]uint{billingCfgStruct.TenantID}, []uint{billingCfgStruct.BillingConfigID}, billedMonth.Year(), int(billedMonth.Month()), false)
	case entity.BillingConfigType_MarketplaceReseller, entity.BillingConfigType_DirectCustomer:
		log.Info("need to retrieve costs from managing billing configs", zap.String("billingConfigType", billingCfgStruct.Type), zap.Uint("tenantID", billingCfgStruct.TenantID))
		billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetManagingBillingConfigs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, err
		}
		if len(billingConfigs) == 0 {
			log.Info("no managing billing configs", zap.Uint("tenantID", billingCfgStruct.TenantID))
			return nil, nil
		}
		billingConfigIDs := make([]uint, len(billingConfigs))
		for i, b := range billingConfigs {
			billingConfigIDs[i] = b.BillingConfigID
		}
		return model.GetPartnerBillsDetailsWithCache([]uint{billingCfgStruct.TenantID}, billingConfigIDs, billedMonth.Year(), int(billedMonth.Month()), false)
	case entity.BillingConfigType_Alicloud:
		tenantIDs, billingConfigIDs, err := listAliCustomerTenantIDsAndBillingConfigIDs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, err
		}
		return model.GetPartnerBillsDetailsWithCache(tenantIDs, billingConfigIDs, billedMonth.Year(), int(billedMonth.Month()), true)
	default:
		return nil, billing_errors.ObjectNotExistsError
	}
}

// @Id TCPN_ExportUsageCSV
// @Summary Export Usage CSV
// @Tags PartnerPrivateAPI
// @Description Export Usage CSV
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Produce text/csv
// @Param billedMonth path string true "The month of this bill happens for the specified organization, in the format of `YYYY-MM`. For example `2024-08`."
// @Success 200 {object} []byte
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsDetails/{billedMonth}/export [GET]
func (s *costExplorerOpenAPI) GetPartnerBillsDetailsCSV(c *gin.Context) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	defer func() {
		auditloghandler.Log(auditlog.EventDownloadBill, c, billingCfgStruct.TenantID, nil, err)
	}()
	r, err := s.GetPartnerBillsDetails(c)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	csvResult, err := csvFromPartnerBillsDetails(billingCfgStruct.TenantID, r.(*forms.PartnerBillsDetails))
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	writer := &bytes.Buffer{}
	err = csv.NewWriter(writer).WriteAll(csvResult)
	if err != nil {
		ginutils.ReturnError(c, billing_errors.NewSystemError(err))
		return
	}

	c.Data(http.StatusOK, "application/octet-stream", writer.Bytes())
}

func csvFromPartnerBillsDetails(tenantID uint, costs *forms.PartnerBillsDetails) ([][]string, error) {
	var csvLine [][]string
	var header1, header2, header3, header4, header5 []string

	header1 = append(header1, "Billing Period", timeutils.GetMonthPeriodByYearMonth(costs.Year, costs.Month))

	tenantInfo, err := centralhandler.GetCentralHandler().GetTenant(tenantID)
	if err != nil {
		return nil, err
	}
	header2 = append(header2, "Organization Name", tenantInfo.Name)
	header3 = append(header3, "Organization ID", strconv.Itoa(int(tenantID)))
	header4 = nil
	header5 = append(header5, "Billed Date", "Customer ID", "Customer Name", "Service", "Project ID", "Project Name",
		"Cluster ID", "Cluster Name", "Running Total", "Discounts", "Credits", "Total Cost")

	csvLine = append(csvLine, header1, header2, header3, header4, header5)
	var precision int32 = 6
	for _, usageDetail := range costs.BillsDetails {
		var oneLine []string
		runningTotal := usageDetail.RunningTotal.Add(usageDetail.RunningTotalTruncatedLoss).Truncate(precision)
		credits := usageDetail.Credits.Add(usageDetail.CreditsTruncatedLoss).Truncate(precision)
		totalCost := usageDetail.TotalCost.Add(usageDetail.TotalCostTruncatedLoss).Truncate(precision)
		discount := runningTotal.Sub(credits).Sub(totalCost)
		oneLine = append(oneLine, usageDetail.BilledDate, usageDetail.CustomerID, usageDetail.CustomerName, usageDetail.ServicePathName, usageDetail.ProjectID,
			usageDetail.ProjectName, usageDetail.ClusterID, usageDetail.ClusterName,
			runningTotal.String(),
			discount.String(),
			credits.String(),
			totalCost.String(),
		)
		csvLine = append(csvLine, oneLine)
	}

	return csvLine, nil
}

// @Id Billing_GetPartnerTrendsArgs
// @Tags Partner, PartnerBilling
// @Summary Get basic information of a specific organization, which contains the key information to execute a cost explorer query
// @Description This endpoint retrieves the basic information for a specific organization, including service details, cloud provider information, project information, and cluster information.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Success 200 {object} forms.PartnerTrendsArgs
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsCostExplorerArgs [Get]
func (s *costExplorerOpenAPI) GetPartnerTrendsArgs(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	tenantID := billingCfgStruct.TenantID
	funcName := "GetPartnerTrendsArgs"
	var ret forms.PartnerTrendsArgs
	if cachehandler.Get().Get(funcName, &ret, tenantID) {
		return ret, nil
	}
	clusters, err := costexplorer.GetCostExplorerClusters(tenantID)
	if err != nil {
		return nil, err
	}
	projects, err := costexplorer.GetCostExplorerProjects(tenantID)
	if err != nil {
		return nil, err
	}
	providers, err := costexplorer.GetCostExplorerProviders()
	if err != nil {
		return nil, err
	}
	services, err := costexplorer.GetCostExplorerServices()
	if err != nil {
		return nil, err
	}
	customers, err := costexplorer.GetCostExplorerCustomers(tenantID)
	if err != nil {
		return nil, err
	}
	productsSet := make(map[string]*struct{})
	for _, svc := range services {
		productsSet[*svc.Product] = nil
	}
	var clustersOpenAPI []*forms.ClusterOpenAPI
	for _, cluster := range clusters {
		clustersOpenAPI = append(clustersOpenAPI, &forms.ClusterOpenAPI{
			ID:         cluster.ID,
			ProjectID:  cluster.ProjectID,
			RegionName: cluster.RegionName,
			Name:       cluster.Name,
			Status:     cluster.Status,
		})
	}
	var projectsOpenAPI []*forms.ProjectOpenAPI
	for _, project := range projects {
		projectsOpenAPI = append(projectsOpenAPI, &forms.ProjectOpenAPI{
			ID:     project.ID,
			Name:   project.Name,
			Status: project.Status,
		})
	}
	ret = forms.PartnerTrendsArgs{
		TrendsArgs: forms.TrendsArgs{
			Services:  services,
			Providers: providers,
			Clusters:  clustersOpenAPI,
			Projects:  projectsOpenAPI,
		},
		Customers: customers,
	}
	cachehandler.Get().Set(funcName, &ret, tenantID)
	return &ret, nil
}

// @Id Billing_PartnerTrendsOpenAPI
// @Tags Partner, PartnerBilling
// @Summary Get the usage details according to a cost explorer query
// @Description This endpoint executes a cost explorer query and returns the usage details. Cost explorer query:
// @Description 1. Allow users to group costs by category
// @Description 2. Allow users to focus on a specific cost item by filter
// @Description 3. Allow users to query cost in the specific time range
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param args body forms.PartnerTrendsInput true "Query arguments"
// @Success 200 {object} forms.PartnerBillsCostExplorer
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsCostExplorer [POST]
func (s *costExplorerOpenAPI) PartnerTrendsOpenAPI(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	tenantID, billingConfigID := billingCfgStruct.TenantID, billingCfgStruct.BillingConfigID
	query, err := parseTrendsQuery(c, tenantID, true)
	if err != nil {
		return nil, err
	}
	tenantIDs := []uint{tenantID}
	var billingConfigIDs []uint
	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSP, entity.BillingConfigType_ResalePartner:
		billingConfigIDs = []uint{billingConfigID}
	case entity.BillingConfigType_MarketplaceReseller, entity.BillingConfigType_DirectCustomer:
		log.Info("need to retrieve costs from managing billing configs", zap.String("billingConfigType", billingCfgStruct.Type), zap.Uint("tenantID", billingCfgStruct.TenantID))
		billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetManagingBillingConfigs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, err
		}
		if len(billingConfigs) == 0 {
			log.Info("no managing billing configs", zap.Uint("tenantID", billingCfgStruct.TenantID))
			return nil, nil
		}
		billingConfigIDs = make([]uint, len(billingConfigs))
		for i, b := range billingConfigs {
			billingConfigIDs[i] = b.BillingConfigID
		}
	case entity.BillingConfigType_Alicloud:
		tenantIDs, billingConfigIDs, err = listAliCustomerTenantIDsAndBillingConfigIDs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, err
		}
	}
	var ret *forms.CostExploreTrendsOutput
	timeSpan := int(query.EndDate.Sub(*query.StartDate).Hours()) / 24
	if timeSpan > config.Get().GetInt(config.CostExplorerTrendsLongTimeSpanQueryThresholdDays) {
		semaphore.ExecutionWithSemaphore(costExploreTrendsLongTimeSpanSem, func() {
			ret, err = costexplorer.CostTrendsWithMultiTenantsMultiBillingConfigs(tenantIDs, billingConfigIDs, query)
		})
	} else {
		ret, err = costexplorer.CostTrendsWithMultiTenantsMultiBillingConfigs(tenantIDs, billingConfigIDs, query)
	}
	if err != nil {
		return nil, err
	}
	return copyPartnerBillsCostExplorerFromCostExploreTrendsOutput(ret)
}

// @Id TCPN_GetPartnerTrendsCSV
// @Tags PartnerPrivateAPI
// @Summary Get the usage details according to a cost explorer query
// @Description This endpoint executes a cost explorer query and returns the usage details. Cost explorer query:
// @Description 1. Allow users to group costs by category
// @Description 2. Allow users to focus on a specific cost item by filter
// @Description 3. Allow users to query cost in the specific time range
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param withDiscountsAndCredits query bool true "withDiscountsAndCredits"
// @Param args body forms.PartnerTrendsInput true "Query arguments"
// @Produce text/csv
// @Success 200 {object} []byte
// @Failure 400  {object}  billing_errors.BaseError
// @Router /billsCostExplorer/export [POST]
func (s *costExplorerOpenAPI) GetPartnerTrendsCSV(c *gin.Context) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	tenantID, billingConfigID := billingCfgStruct.TenantID, billingCfgStruct.BillingConfigID
	trendsQueryInput, err := parseTrendsQuery(c, tenantID, true)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	var q struct {
		DisplayCreditsAndDiscountOnCSV bool `form:"withDiscountsAndCredits"`
	}
	err = c.ShouldBindQuery(&q)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	query := &forms.CostExploreTrendsCSVInput{
		CostExploreTrendsInput:         *trendsQueryInput,
		DisplayCreditsAndDiscountOnCSV: q.DisplayCreditsAndDiscountOnCSV,
	}
	tenantIDs := []uint{tenantID}
	var billingConfigIDs []uint
	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSP, entity.BillingConfigType_ResalePartner:
		billingConfigIDs = []uint{billingConfigID}
	case entity.BillingConfigType_MarketplaceReseller, entity.BillingConfigType_DirectCustomer:
		log.Info("need to retrieve costs from managing billing configs", zap.String("billingConfigType", billingCfgStruct.Type), zap.Uint("tenantID", billingCfgStruct.TenantID))
		billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetManagingBillingConfigs(billingCfgStruct.TenantID)
		if err != nil {
			ginutils.ReturnError(c, err)
			return
		}
		if len(billingConfigs) == 0 {
			log.Info("no managing billing configs", zap.Uint("tenantID", billingCfgStruct.TenantID))
			return
		}
		billingConfigIDs = make([]uint, len(billingConfigs))
		for i, b := range billingConfigs {
			billingConfigIDs[i] = b.BillingConfigID
		}
	case entity.BillingConfigType_Alicloud:
		tenantIDs, billingConfigIDs, err = listAliCustomerTenantIDsAndBillingConfigIDs(billingCfgStruct.TenantID)
		if err != nil {
			ginutils.ReturnError(c, billing_errors.NewSystemError(err))
			return
		}
	}
	var ret []byte
	timeSpan := int(query.EndDate.Sub(*query.StartDate).Hours()) / 24
	if timeSpan > config.Get().GetInt(config.CostExplorerTrendsLongTimeSpanQueryThresholdDays) {
		semaphore.ExecutionWithSemaphore(costExploreTrendsLongTimeSpanSem, func() {
			ret, err = costexplorer.CostTrendsCSVWithMultiTenantsMultiBillingConfigs(tenantIDs, billingConfigIDs, query)
		})
	} else {
		ret, err = costexplorer.CostTrendsCSVWithMultiTenantsMultiBillingConfigs(tenantIDs, billingConfigIDs, query)
	}
	if err != nil {
		ginutils.ReturnError(c, billing_errors.NewSystemError(err))
		return
	}
	c.Data(http.StatusOK, "text/csv", ret)
}

func copyBillsCostExplorerFromCostExploreTrendsOutput(ret *forms.CostExploreTrendsOutput) (forms.BillsCostExplorer, error) {
	var res forms.BillsCostExplorer
	res.RunningTotal = ret.RawPrice
	res.Discounts = ret.Discount
	res.Credits = ret.CreditsUsage
	res.TotalCost = ret.CostPrice
	for _, el := range ret.Elements {
		var breakdown []forms.BillsCostExplorerBreakdown
		for _, br := range el.Breakdown {
			breakdown = append(breakdown, forms.BillsCostExplorerBreakdown{
				BilledDate: br.BillingDate,
				DetailName: br.DetailName,
				DetailKey:  br.DetailKey,
				BillsCosts: forms.BillsCosts{
					RunningTotal: br.RawPrice,
					Discounts:    br.Discount,
					Credits:      br.CreditsUsage,
					TotalCost:    br.CostPrice,
				},
			})
		}
		res.Elements = append(res.Elements, forms.BillsCostExplorerElements{
			BillsCosts: forms.BillsCosts{
				RunningTotal: el.RawPrice,
				Discounts:    el.Discount,
				Credits:      el.CreditsUsage,
				TotalCost:    el.CostPrice,
			},
			BilledDate: el.BillingDate,
			Breakdown:  breakdown,
		})

	}

	return res, nil
}

func copyPartnerBillsCostExplorerFromCostExploreTrendsOutput(ret *forms.CostExploreTrendsOutput) (forms.PartnerBillsCostExplorer, error) {
	var res forms.PartnerBillsCostExplorer
	res.RunningTotal = ret.RawPrice
	res.Discounts = ret.Discount
	res.Credits = ret.CreditsUsage
	res.TotalCost = ret.CostPrice
	for _, el := range ret.Elements {
		var breakdown []forms.PartnerBillsCostExplorerBreakdown
		for _, br := range el.Breakdown {
			breakdown = append(breakdown, forms.PartnerBillsCostExplorerBreakdown{
				BilledDate: br.BillingDate,
				DetailName: br.DetailName,
				DetailKey:  br.DetailKey,
				BillsCosts: forms.BillsCosts{
					RunningTotal: br.RawPrice,
					Discounts:    br.Discount,
					Credits:      br.CreditsUsage,
					TotalCost:    br.CostPrice,
				},
			})
		}
		res.Elements = append(res.Elements, forms.PartnerBillsCostExplorerElements{
			BillsCosts: forms.BillsCosts{
				RunningTotal: el.RawPrice,
				Discounts:    el.Discount,
				Credits:      el.CreditsUsage,
				TotalCost:    el.CostPrice,
			},
			BilledDate: el.BillingDate,
			Breakdown:  breakdown,
		})

	}

	return res, nil
}

// @Id Billing_GetPartnerInvoicedBills
// @Tags PartnerPrivateAPI
// @Summary Get organization monthly bills based on invoice sent date
// @Description This endpoint retrieves the bill for the given month of a specific organization based on the invoice sent date. The bill is returned in JSON format.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param billedMonth path string true "The month for which to retrieve the bill, in the format of `YYYY-MM`. This refers to the month when invoices were sent."
// @Success 200 {object} forms.PartnerBillsExplorer
// @Failure 400  {object}  billing_errors.BaseError
// @Router /invoicedBills/{billedMonth} [GET]
func (s *costExplorerOpenAPI) GetPartnerInvoicedBills(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}

	tenantIDs, billingConfigIDs, tenantIDAsCustomer, err := s.getPartnerQueryArgs(c, billingCfgStruct)
	if err != nil {
		return nil, err
	}

	model := costexplorer.NewInvoicedCostExplorerModel()
	return model.CalculatePartnerOpenAPIMonthlyCostBySentDateWithCache(tenantIDs, billingConfigIDs, billedMonth, tenantIDAsCustomer)
}

// @Id Billing_GetPartnerInvoicedBillsDetails
// @Tags PartnerPrivateAPI
// @Summary Get the monthly bill of the organization with daily usage details based on invoice sent date
// @Description This endpoint retrieves the bill for the given month of a specific organization with daily usage details, based on the invoice sent date.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param billedMonth path string true "The month of this bill happens for the specified organization, in the format of `YYYY-MM`. This refers to the month when invoices were sent."
// @Success 200 {object} forms.PartnerInvoicedBillsDetails
// @Failure 400  {object}  billing_errors.BaseError
// @Router /invoicedBillsDetails/{billedMonth} [GET]
func (s *costExplorerOpenAPI) GetPartnerInvoicedBillsDetails(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query := &OpenapiMonthlyCostURI{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	billedMonth, err := time.Parse("2006-01", query.BilledMonth)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	tenantID := billingCfgStruct.TenantID
	defer func() {
		auditloghandler.Log(auditlog.EventShowBill, c, tenantID, nil, err)
	}()

	tenantIDs, billingConfigIDs, tenantIDAsCustomer, err := s.getPartnerQueryArgs(c, billingCfgStruct)
	if err != nil {
		return nil, err
	}

	model := costexplorer.NewInvoicedCostExplorerModel()
	return model.GetPartnerBillsDetailsBySentDateWithCache(tenantIDs, billingConfigIDs, billedMonth.Year(), int(billedMonth.Month()), tenantIDAsCustomer)
}

// @Id TCPN_ExportInvoicedUsageCSV
// @Summary Export Usage CSV based on invoice sent date
// @Tags PartnerPrivateAPI
// @Description Export Usage CSV based on the invoice sent date.
// @Schemes
// @Accept json
// @Produce text/csv
// @Param accessOrgId query string true "org_id"
// @Param billedMonth path string true "The month of this bill happens for the specified organization, in the format of `YYYY-MM`. This refers to the month when invoices were sent."
// @Success 200 {object} []byte
// @Failure 400  {object}  billing_errors.BaseError
// @Router /invoicedBillsDetails/{billedMonth}/export [GET]
func (s *costExplorerOpenAPI) GetPartnerInvoicedBillsDetailsCSV(c *gin.Context) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	defer func() {
		auditloghandler.Log(auditlog.EventDownloadBill, c, billingCfgStruct.TenantID, nil, err)
	}()

	r, err := s.GetPartnerInvoicedBillsDetails(c)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}

	costs := r.(*forms.PartnerInvoicedBillsDetails)
	csvResult, err := s.csvFromPartnerInvoicedBillsDetails(billingCfgStruct.TenantID, costs)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}

	writer := &bytes.Buffer{}
	err = csv.NewWriter(writer).WriteAll(csvResult)
	if err != nil {
		ginutils.ReturnError(c, billing_errors.NewSystemError(err))
		return
	}

	c.Data(http.StatusOK, "application/octet-stream", writer.Bytes())
}

// Helper function to handle common logic for partner query arguments
func (s *costExplorerOpenAPI) getPartnerQueryArgs(c *gin.Context, billingCfgStruct *forms.BillingConfig) ([]uint, []uint, bool, error) {
	var tenantIDs []uint
	var billingConfigIDs []uint
	tenantIDAsCustomer := false
	var err error
	fmt.Println(billingCfgStruct.Type)
	switch billingCfgStruct.Type {
	case entity.BillingConfigType_MSP, entity.BillingConfigType_ResalePartner:
		tenantIDs = []uint{billingCfgStruct.TenantID}
		billingConfigIDs = []uint{billingCfgStruct.BillingConfigID}
	case entity.BillingConfigType_MarketplaceReseller, entity.BillingConfigType_DirectCustomer:
		log.Info("need to retrieve costs from managing billing configs", zap.String("billingConfigType", billingCfgStruct.Type), zap.Uint("tenantID", billingCfgStruct.TenantID))
		billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetManagingBillingConfigs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, nil, false, err
		}
		if len(billingConfigs) == 0 {
			log.Info("no managing billing configs", zap.Uint("tenantID", billingCfgStruct.TenantID))
			return nil, nil, false, nil
		}
		billingConfigIDs = make([]uint, len(billingConfigs))
		for i, b := range billingConfigs {
			billingConfigIDs[i] = b.BillingConfigID
		}
		tenantIDs = []uint{billingCfgStruct.TenantID}
	case entity.BillingConfigType_Alicloud:
		tenantIDs, billingConfigIDs, err = listAliCustomerTenantIDsAndBillingConfigIDs(billingCfgStruct.TenantID)
		if err != nil {
			return nil, nil, false, err
		}
		tenantIDAsCustomer = true
	default:
		return nil, nil, false, billing_errors.ObjectNotExistsError
	}
	fmt.Println(tenantIDs, billingConfigIDs, tenantIDAsCustomer)
	return tenantIDs, billingConfigIDs, tenantIDAsCustomer, nil
}

// Helper function to generate CSV from PartnerInvoicedBillsDetails
func (s *costExplorerOpenAPI) csvFromPartnerInvoicedBillsDetails(tenantID uint, costs *forms.PartnerInvoicedBillsDetails) ([][]string, error) {
	var csvLine [][]string
	var header1, header2, header3, header4, header5 []string

	header1 = append(header1, "Billing Period", timeutils.GetMonthPeriodByYearMonth(costs.Year, costs.Month))

	tenantInfo, err := centralhandler.GetCentralHandler().GetTenant(tenantID)
	if err != nil {
		return nil, err
	}
	header2 = append(header2, "Organization Name", tenantInfo.Name)
	header3 = append(header3, "Organization ID", strconv.Itoa(int(tenantID)))
	header4 = nil
	header5 = append(header5, "Billed Date", "Invoiced Date", "Payment Code", "Customer ID", "Customer Name", "Service", "Project ID", "Project Name",
		"Cluster ID", "Cluster Name", "Running Total", "Discounts", "Credits", "Total Cost")

	csvLine = append(csvLine, header1, header2, header3, header4, header5)
	var precision int32 = 6
	for _, usageDetail := range costs.BillsDetails {
		var oneLine []string
		runningTotal := usageDetail.RunningTotal.Add(usageDetail.RunningTotalTruncatedLoss).Truncate(precision)
		credits := usageDetail.Credits.Add(usageDetail.CreditsTruncatedLoss).Truncate(precision)
		totalCost := usageDetail.TotalCost.Add(usageDetail.TotalCostTruncatedLoss).Truncate(precision)
		discount := runningTotal.Sub(credits).Sub(totalCost)
		oneLine = append(oneLine, usageDetail.BilledDate, usageDetail.InvoicedDate, usageDetail.PaymentCode, usageDetail.CustomerID, usageDetail.CustomerName, usageDetail.ServicePathName, usageDetail.ProjectID,
			usageDetail.ProjectName, usageDetail.ClusterID, usageDetail.ClusterName,
			runningTotal.String(),
			discount.String(),
			credits.String(),
			totalCost.String(),
		)
		csvLine = append(csvLine, oneLine)
	}

	return csvLine, nil
}

func InitializeOpenAPICostExplorerRouter(router ginutils.MyRoute) {
	s := &costExplorerOpenAPI{}
	router.HandleWithError("GET", "/tenants/:tenant_id/bills/:billedMonth", s.BillsExplorerOpenAPI)
	router.HandleWithError("GET", "/bills/:billedMonth", s.BillsExplorerOpenAPI)
	router.HandleWithError("GET", "/tenants/:tenant_id/billsDetails/:billedMonth", s.BillsDetailsOpenAPI)
	router.HandleWithError("GET", "/billsDetails/:billedMonth", s.BillsDetailsOpenAPI)
	router.HandleWithError("GET", "/tenants/:tenant_id/billsCostExplorer/args", s.TrendsArgsOpenAPI)
	router.HandleWithError("GET", "/billsCostExplorer/args", s.TrendsArgsOpenAPI)
	router.HandleWithError("POST", "/tenants/:tenant_id/billsCostExplorer", s.TrendsOpenAPI)
	router.HandleWithError("POST", "/billsCostExplorer", s.TrendsOpenAPI)
	router.HandleWithError("GET", "/tenants/:tenant_id/bills/:billedMonth/clusters/:cluster_id", s.ClusterBillsExplorerOpenAPI)
	router.HandleWithError("GET", "/bills/:billedMonth/clusters/:cluster_id", s.ClusterBillsExplorerOpenAPI)
	router.HandleWithError("GET", "/tenants/:tenant_id/billsDetails/:billedMonth/clusters/:cluster_id", s.ClusterBillsDetailsOpenAPI)
	router.HandleWithError("GET", "/billsDetails/:billedMonth/clusters/:cluster_id", s.ClusterBillsDetailsOpenAPI)
}

func InitializeCostExplorerForPartnerOpenAPIRouter(router ginutils.MyRoute) {
	s := &costExplorerOpenAPI{}
	router.HandleWithError("GET", "/bills/:billedMonth", s.GetPartnerBills)
	router.HandleWithError("GET", "/billsDetails/:billedMonth", s.GetPartnerBillsDetails)
	router.HandleWithError("GET", "/billsCostExplorerArgs", s.GetPartnerTrendsArgs)
	router.HandleWithError("POST", "/billsCostExplorer", s.PartnerTrendsOpenAPI)
}

func InitializeCostExplorerForPartnerPrivateAPIRouter(router ginutils.MyRoute) {
	s := &costExplorerOpenAPI{}
	router.Handle("GET", "/billsDetails/:billedMonth/export", s.GetPartnerBillsDetailsCSV)
	router.Handle("POST", "/billsCostExplorer/export", s.GetPartnerTrendsCSV)
	router.HandleWithError("GET", "/invoicedBills/:billedMonth", s.GetPartnerInvoicedBills)
	router.HandleWithError("GET", "/invoicedBillsDetails/:billedMonth", s.GetPartnerInvoicedBillsDetails)
	router.Handle("GET", "/invoicedBillsDetails/:billedMonth/export", s.GetPartnerInvoicedBillsDetailsCSV)
}

func InitializeS3BillsDetailOpenAPIRouter(router ginutils.MyRoute) {
	s := &s3BillsDetailAPI{}
	router.HandleWithError("GET", "/billsDetails/:billedMonth/presignUrl", s.GetBillsDetailPresignURLOpenAPI)
	router.HandleWithError("GET", "/billsDetails/daily/:billedDate/presignUrl", s.GetDailyBillsDetailPresignURLOpenAPI)
}
