package api

import (
	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/auditloghandler"
	"github.com/pingcap/dbaas/billing2/model/payment/paymentsub"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/tidbcloud/tiinf/sdk/auditlog"
	"go.uber.org/zap"
)

type billingInfoAPI struct{}

// @Summary SyncBillingInformationOnCardBinding
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param Query body forms.BillingInformationInput true "BillingInformation"
// @Success 200 {object} forms.BillingInformation
// @Router /tenants/{tenant_id}/billing_info/sync_on_card_binding [post]
func (s *billingInfoAPI) SyncBillingInformationOnCardBinding(c *gin.Context) (ret ginutils.HandlerFuncResult, retErr error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	info := &forms.BillingInformation{}
	if err := c.ShouldBindJSON(info); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	defer func() {
		auditloghandler.Log(auditlog.EventEditBillingProfile, c, tenantID, auditloghandler.BillingInformation2AuditLogDetails(info), retErr)
	}()
	model := &paymentsub.BillingInformationModel{}
	_, err = model.CreateDefaultBillingInfo(tenantID, info)
	if err != nil {
		log.Error("failed to update default billing info", zap.Uint("TenantID", tenantID), zap.Error(err))
	} else {
		log.Info("synced default billing info from stripe confirm", zap.Uint("TenantID", tenantID))
	}
	return model.GetBillingInfo(tenantID)
}

// @Summary Update BillingInfo
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param Query body forms.BillingInformationInput true "BillingInformation"
// @Success 200 {object} forms.BillingInformation
// @Router /tenants/{tenant_id}/billing_info [put]
func (s *billingInfoAPI) UpdateBillingInformation(c *gin.Context) (ret ginutils.HandlerFuncResult, retErr error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	info := &forms.BillingInformation{}
	if err := c.ShouldBindJSON(info); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	defer func() {
		auditloghandler.Log(auditlog.EventEditBillingProfile, c, tenantID, auditloghandler.BillingInformation2AuditLogDetails(info), retErr)
	}()
	model := &paymentsub.BillingInformationModel{}
	return model.CreateOrUpdateBillingInfo(tenantID, info)
}

// @Summary Get BillingInfo
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.BillingInformation
// @Router /tenants/{tenant_id}/billing_info [get]
func (s *billingInfoAPI) GetBillingInfo(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	model := &paymentsub.BillingInformationModel{}

	info, err := model.GetBillingInfo(tenantID)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary List Billing Company
// @Schemes
// @Accept json
// @Produce json
// @Success 200 {object} []forms.BillingCompany
// @Router /billing_company [get]
func (s *billingInfoAPI) ListBillingCompany(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	model := &paymentsub.BillingInformationModel{}
	return model.ListBillingCompany()
}

// @Summary Get Tenant BillingCompany
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.TenantBillingCompany
// @Router /tenants/{tenant_id}/billing_company [get]
func (s *billingInfoAPI) GetTenantBillingCompany(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	model := &paymentsub.BillingInformationModel{}

	return model.GetTenantBillingCompany(tenantID)
}

// @Summary Update Tenant BillingCompany
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param Query body forms.TenantBillingCompanyInput true "TenantBillingCompany"
// @Success 200 {object} forms.TenantBillingCompany
// @Router /tenants/{tenant_id}/billing_company [put]
func (s *billingInfoAPI) UpdateTenantBillingCompany(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	company := &forms.TenantBillingCompany{}
	if err := c.ShouldBindJSON(company); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	model := &paymentsub.BillingInformationModel{}

	return model.UpdateTenantBillingCompany(tenantID, company)
}

// @Id Billing_GetOrgBillingProfile
// @Summary Get organization billing profile
// @Tags Partner, PartnerBilling
// @Description This endpoint retrieves the billing profile for an organization.
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Success 200 {object} forms.BillingProfileForOpenAPI
// @Router /billingProfile [get]
func (s *billingInfoAPI) GetOrgBillingProfile(c *gin.Context) (ret ginutils.HandlerFuncResult, retErr error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}

	model := &paymentsub.BillingInformationModel{}

	info, err := model.GetBillingInfo(billingCfgStruct.TenantID)
	if err != nil {
		return nil, err
	}
	res := forms.BillingProfileForOpenAPI{
		CompanyName:  info.CompanyName,
		BillingEmail: info.BillingEmail,
		CountryCode:  info.CountryCode,
		State:        info.State,
		City:         info.City,
		Line1:        info.Line1,
		Line2:        info.Line2,
		PostalCode:   info.PostalCode,
		TaxId:        info.TaxID,
	}
	if info.TaxIDType != "" {
		res.TaxIdType = forms.TaxIDType(info.TaxIDType)
	}
	return res, nil
}

// @Id Billing_UpdateOrgBillingProfile
// @Summary Update organization billing profile
// @Tags Partner, PartnerBilling
// @Description This endpoint updates the billing profile for an organization.
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param billingProfile body forms.BillingProfileForOpenAPI true "billing profile"
// @Success 200 {object} forms.BillingProfileForOpenAPI
// @Router /billingProfile [patch]
func (s *billingInfoAPI) UpdateOrgBillingProfile(c *gin.Context) (ret ginutils.HandlerFuncResult, retErr error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	tenantID := billingCfgStruct.TenantID
	input := &forms.BillingProfileForOpenAPI{}
	if err := c.ShouldBindJSON(input); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	info := &forms.BillingInformation{
		BillingAddress: forms.BillingAddress{
			CompanyName:  input.CompanyName,
			BillingEmail: input.BillingEmail,
			CountryCode:  input.CountryCode,
			State:        input.State,
			City:         input.City,
			Line1:        input.Line1,
			Line2:        input.Line2,
			PostalCode:   input.PostalCode,
		},
		TaxInfo: forms.TaxInfo{
			TaxID:     input.TaxId,
			TaxIDType: string(input.TaxIdType),
		},
	}
	defer func() {
		auditloghandler.Log(auditlog.EventEditBillingProfile, c, tenantID, auditloghandler.BillingInformation2AuditLogDetails(info), retErr)
	}()
	model := &paymentsub.BillingInformationModel{}
	return model.CreateOrUpdateBillingInfo(tenantID, info)
}

func InitializeBillingInfoRouter(router ginutils.MyRoute) {
	s := &billingInfoAPI{}
	router.HandleWithError("GET", "/tenants/:tenant_id/billing_info", s.GetBillingInfo)
	router.HandleWithError("POST", "/tenants/:tenant_id/billing_info/sync_on_card_binding", s.SyncBillingInformationOnCardBinding)
	router.HandleWithError("PUT", "/tenants/:tenant_id/billing_info", s.UpdateBillingInformation)
	router.HandleWithError("GET", "/billing_company", s.ListBillingCompany)
	router.HandleWithError("GET", "/tenants/:tenant_id/billing_company", s.GetTenantBillingCompany)
	router.HandleWithError("PUT", "/tenants/:tenant_id/billing_company", s.UpdateTenantBillingCompany)
}

func InitializeBillingInfoRouterForOpenAPI(router ginutils.MyRoute) {
	s := &billingInfoAPI{}
	router.HandleWithError("GET", "/billingProfile", s.GetOrgBillingProfile)
	router.HandleWithError("PATCH", "/billingProfile", s.UpdateOrgBillingProfile)
}
