package api

import (
	"net/http"
	"path/filepath"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
)

var subcriptionModel = &billing.SubscriptionModel{}

type SubscriptionURIStruct struct {
	TenantID       uint `uri:"tenant_id" binding:"required"`
	SubscriptionID uint `uri:"subscription_id" binding:"required"`
}

// @Summary Create Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param Subscription body forms.SubscriptionInput true "Subscription"
// @Success 200 {object} forms.Subscription
// @Router /tenants/{tenant_id}/subcriptions [post]
func (s *API) CreateSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	subscription := &forms.Subscription{}
	err = c.ShouldBindJSON(&subscription)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.CreateSubscription(tenantID, subscription)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary List All Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} []forms.Subscription
// @Router /tenants/{tenant_id}/subcriptions [get]
func (s *API) ListAllSubscriptions(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	infos, err := subcriptionModel.ListSubscriptions(tenantID)
	if err != nil {
		return nil, err
	}
	return infos, nil
}

// @Summary List Submited Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} []forms.Subscription
// @Router /tenants/{tenant_id}/submited_subcriptions [get]
func (s *API) ListSubmitedSubscriptions(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	infos, err := subcriptionModel.ListSubmitedSubscriptions(tenantID)
	if err != nil {
		return nil, err
	}
	return infos, nil
}

// @Summary Update Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param subscription_id path string true "subscription_id"
// @Param Subscription body forms.SubscriptionInput true "Subscription"
// @Success 200 {object} forms.Subscription
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id} [put]
func (s *API) UpdateSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	subscription := &forms.Subscription{}
	err = c.ShouldBindJSON(&subscription)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.UpdateSubscription(subscriptionURIStruct.SubscriptionID, subscription)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Get Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param subscription_id path string true "subscription_id"
// @Success 200 {object} forms.Subscription
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id} [get]
func (s *API) GetSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.GetSubscription(subscriptionURIStruct.SubscriptionID)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Delete Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param subscription_id path string true "subscription_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id} [delete]
func (s *API) DeleteSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	err = subcriptionModel.DeleteSubscription(subscriptionURIStruct.SubscriptionID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Accept Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param subscription_id path string true "subscription_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id}/actions/accept [post]
func (s *API) AccpetSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	subscriptionChange := &forms.SubscriptionChange{}
	err = c.ShouldBindJSON(&subscriptionChange)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.AcceptSubscription(subscriptionURIStruct.SubscriptionID, subscriptionChange)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Reject Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param subscription_id path string true "subscription_id"
// @Param SubscriptionChange body forms.SubscriptionChange true "SubscriptionChange"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id}/actions/reject [post]
func (s *API) RejectSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	subscriptionChange := &forms.SubscriptionChange{}
	err = c.ShouldBindJSON(&subscriptionChange)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.RejectSubscription(subscriptionURIStruct.SubscriptionID, subscriptionChange)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Suspend Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param SubscriptionChange body forms.SubscriptionChange true "SubscriptionChange"
// @Param subscription_id path string true "subscription_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id}/actions/suspend [post]
func (s *API) SuspendSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.SuspendSubscription(subscriptionURIStruct.SubscriptionID)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Submit Subscription
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param SubscriptionChange body forms.SubscriptionChange true "SubscriptionChange"
// @Param subscription_id path string true "subscription_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id}/actions/submit [post]
func (s *API) SubmitSubscription(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	info, err := subcriptionModel.SubmitSubscription(subscriptionURIStruct.SubscriptionID)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Download Subscription
// @Schemes
// @Param tenant_id path string true "tenant_id"
// @Param subscription_id path string true "subscription_id"
// @Router /tenants/{tenant_id}/subcriptions/{subscription_id}/actions/download [get]
func (s *API) DownloadSubscription(c *gin.Context) {
	subscriptionURIStruct := &SubscriptionURIStruct{}
	err := c.ShouldBindUri(subscriptionURIStruct)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	info, err := subcriptionModel.GetSubscription(subscriptionURIStruct.SubscriptionID)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	file, err := centralhandler.GetCentralHandler().DownloadFile(info.FilePath)
	if err != nil {
		ginutils.ReturnError(c, err)
		return
	}
	_, fileName := filepath.Split(info.FilePath)

	c.Header("Content-Disposition", "attachment; filename="+fileName)
	c.Data(http.StatusOK, config.Get().GetString(config.CDNFileContentType), file)
}

func InitializeSubscriptionRouter(router ginutils.MyRoute) {
	s := &API{}
	router.HandleWithError("GET", "/tenants/:tenant_id/subscriptions", s.ListAllSubscriptions)
	router.HandleWithError("GET", "/tenants/:tenant_id/submited_subscriptions", s.ListSubmitedSubscriptions)
	router.HandleWithError("POST", "/tenants/:tenant_id/subscriptions", s.CreateSubscription)
	router.HandleWithError("GET", "/tenants/:tenant_id/subscriptions/:subscription_id", s.GetSubscription)
	router.HandleWithError("PUT", "/tenants/:tenant_id/subscriptions/:subscription_id", s.UpdateSubscription)
	router.HandleWithError("POST", "/tenants/:tenant_id/subscriptions/:subscription_id/actions/reject", s.RejectSubscription)
	router.HandleWithError("POST", "/tenants/:tenant_id/subscriptions/:subscription_id/actions/accept", s.AccpetSubscription)
	router.HandleWithError("POST", "/tenants/:tenant_id/subscriptions/:subscription_id/actions/suspend", s.SuspendSubscription)
	router.HandleWithError("POST", "/tenants/:tenant_id/subscriptions/:subscription_id/actions/submit", s.SubmitSubscription)
	router.Handle("GET", "/tenants/:tenant_id/subscriptions/:subscription_id/actions/download", s.DownloadSubscription)
}
