package api

import (
	"errors"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/payment"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
)

var invoiceModel = payment.InvoiceModel{}

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

	isHourlyPayment, err := paymentModel.IsHourlyPayment(tenantID)
	if err != nil {
		return nil, err
	}

	if isHourlyPayment {
		return invoiceModel.ListAllInvoicesWithType(tenantID, entity.InvoiceType_COMMITMENT)
	}

	return invoiceModel.ListAllInvoices(tenantID)
}

type InvoiceURIStruct struct {
	TenantID  uint `uri:"tenant_id" binding:"required"`
	InvoiceID uint `uri:"invoice_id" binding:"required"`
}

func getInvoiceURIParams(c *gin.Context) (*InvoiceURIStruct, error) {
	invoiceURIStruct := &InvoiceURIStruct{}
	err := c.ShouldBindUri(invoiceURIStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	return invoiceURIStruct, nil
}

// @Summary Get Invoice
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param invoice_id path string true "invoice_id"
// @Success 200 {object} forms.Invoice
// @Router /tenants/{tenant_id}/invoices/{invoice_id} [get]
func (s *API) GetInvoice(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	invoiceURI, err := getInvoiceURIParams(c)
	if err != nil {
		return nil, err
	}
	invoice, err := invoiceModel.GetInvoice(invoiceURI.InvoiceID)
	if err != nil {
		return nil, err
	}
	return invoice, nil
}

// @Summary Confirm Invoice
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param invoice_id path string true "invoice_id"
// @Success 200 {object} forms.Invoice
// @Router /tenants/{tenant_id}/invoices/{invoice_id}/confirm [put]
func (s *API) ConfirmInvoice(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	invoiceURI, err := getInvoiceURIParams(c)
	if err != nil {
		return nil, err
	}
	invoice, err := invoiceModel.ConfirmInvoice(invoiceURI.InvoiceID)
	if err != nil {
		return nil, err
	}
	return invoice, nil
}

// @Summary Send Invoice
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param invoice_id path string true "invoice_id"
// @Success 200 {object} forms.Invoice
// @Router /tenants/{tenant_id}/invoices/{invoice_id}/send [put]
func (s *API) SendInvoice(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	invoiceURI, err := getInvoiceURIParams(c)
	if err != nil {
		return nil, err
	}
	invoice, err := invoiceModel.SendInvoice(invoiceURI.InvoiceID)
	if err != nil {
		return nil, err
	}
	return invoice, nil
}

// @Summary Create Invoice
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param CreateInvoiceReq body forms.CreateInvoiceReq true "CreateInvoiceReq"
// @Success 200 {object} forms.Invoice
// @Router /tenants/{tenant_id}/invoices [post]
func (s *API) CreateInvoice(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	createInvoiceReq := &forms.CreateInvoiceReq{}
	err = c.ShouldBindJSON(createInvoiceReq)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	if createInvoiceReq.Type != entity.InvoiceType_MONTHLY {
		return nil, billing_errors.NewInvalidInputError(errors.New("unsupported invoice type"))
	}
	invoice, err := invoiceModel.CreateMonthlyInvoice(tenantID, createInvoiceReq.StartDate)
	if err != nil {
		return nil, err
	}
	return invoice, nil
}

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

	invoice, err := invoiceModel.CreateCommitmentInvoice(tenantID, createInvoiceReq)
	if err != nil {
		return nil, err
	}
	return invoice, nil
}

// @Summary Delete Unsent Invoice
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param invoice_id path string true "invoice_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/invoices/{invoice_id} [delete]
func (s *API) DeleteInvoice(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	invoiceURI, err := getInvoiceURIParams(c)
	if err != nil {
		return nil, err
	}
	err = invoiceModel.DeleteInvoice(invoiceURI.InvoiceID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

func InitializeInvoiceRouter(router ginutils.MyRoute) {
	s := &API{}
	router.HandleWithError("GET", "/tenants/:tenant_id/invoices", s.ListAllInvoices)
	router.HandleWithError("POST", "/tenants/:tenant_id/invoices", s.CreateInvoice)
	router.HandleWithError("POST", "/tenants/:tenant_id/commitment_invoices", s.CreateCommitmentInvoice)
	router.HandleWithError("GET", "/tenants/:tenant_id/invoices/:invoice_id", s.GetInvoice)
	router.HandleWithError("PUT", "/tenants/:tenant_id/invoices/:invoice_id/confirm", s.ConfirmInvoice)
	router.HandleWithError("PUT", "/tenants/:tenant_id/invoices/:invoice_id/send", s.SendInvoice)
	router.HandleWithError("DELETE", "/tenants/:tenant_id/invoices/:invoice_id", s.DeleteInvoice)
}
