package mysql

import (
	"context"

	"gorm.io/gorm"

	v1 "github.com/pingcap-inc/pchub/model/v1"
	bfao "github.com/pingcap-inc/pchub/model/v1/fao/billing"
)

type cloudResource struct {
	db *gorm.DB
}

func newCloudResource(ds *datastore) *cloudResource {
	return &cloudResource{db: ds.db}
}

func (c *cloudResource) GetTotal(ctx context.Context, fao *bfao.BillingFAO, dptIds *[]string) (int64, error) {
	var res int64
	d := c.db.
		Table("cloud_resource").
		Where("managementType = 1 and isDel = 0")

	if len(*dptIds) > 0 {
		d.Where("cloud_resource.departLarkID IN (?)", *dptIds)
	}

	if fao.Filter.Owner != "" {
		d.Where("cloud_resource.ownerLarkID = ?", fao.Filter.Owner)
	}
	if fao.Filter.Provider != 0 {
		d.Where("cloud_resource.providerID = ?", fao.Filter.Provider)
	}

	d.Count(&res)

	return res, d.Error
}

func (c *cloudResource) ListAll(ctx context.Context, preloads []string) (*[]v1.CloudResource, error) {
	var res []v1.CloudResource
	d := c.db

	for _, p := range preloads {
		d = d.Preload(p)
	}

	d.Find(&res).Where("classType <> Prod").Order("id asc")

	return &res, dbNotFoundErrorWrap(d.Error)
}

func (c *cloudResource) GetReourcesWithoutBilling(ctx context.Context, fao *bfao.BillingFAO, dptIds *[]string, off, lit int) (*[]v1.CloudResource, error) {
	var res []v1.CloudResource

	d := c.db.
		Model(&v1.CloudResource{}).
		Joins("Owner").
		Joins("Department").
		Joins("Provider").
		Joins("LEFT JOIN ( SELECT * FROM cloud_product_cost WHERE date >= ? AND date < ? ) AS a ON cloud_resource.id = a.resourceID", fao.Filter.DateBetween[0], fao.Filter.DateBetween[1]).
		Where("a.resourceID IS NULL AND cloud_resource.managementType=1")

	if len(*dptIds) > 0 {
		d = d.Where("cloud_resource.departLarkID IN (?)", *dptIds)
	}

	if fao.Filter.Owner != "" {
		d = d.Where("cloud_resource.ownerLarkID = ?", fao.Filter.Owner)
	}

	if fao.Filter.Provider != 0 {
		d = d.Where("cloud_resource.providerID = ?", fao.Filter.Provider)
	}

	d = d.
		Order("cloud_resource.id DESC").
		Limit(lit).
		Offset(off).
		Scan(&res)

	return &res, dbNotFoundErrorWrap(d.Error)
}

func (c *cloudResource) GetByIDs(ctx context.Context, ids []uint64) (*[]v1.CloudResource, error) {
	var res []v1.CloudResource

	d := c.db.
		Joins("Owner").
		Joins("Department").
		Joins("Provider").
		Where("cloud_resource.id IN (?)", ids).
		Find(&res)

	return &res, dbNotFoundErrorWrap(d.Error)
}

func (c *cloudResource) Create(ctx context.Context, cloudResource *v1.CloudResource) (*v1.CloudResource, error) {
	return cloudResource, c.db.Create(cloudResource).Error
}

func (c *cloudResource) Update(ctx context.Context, cloudResource *v1.CloudResource) error {
	return c.db.Model(cloudResource).Updates(v1.CloudResource{Desc: cloudResource.Desc, OwnerLarkID: cloudResource.OwnerLarkID, DepartLarkID: cloudResource.DepartLarkID}).Error
}

func (c *cloudResource) Get(ctx context.Context, cloudResource *v1.CloudResource) (*v1.CloudResource, error) {
	model := c.db.Where("1=1").
		Joins("Owner").
		Joins("Provider").
		Joins("Department").
		Where("managementType = ?", "1")
	if cloudResource.ID != 0 {
		model = model.Where("cloud_resource.id = ?", cloudResource.ID)
	}
	if cloudResource.Name != "" {
		model = model.Where("cloud_resource.name = ?", cloudResource.Name)
	}
	if cloudResource.ManagementID != "" {
		model = model.Where("cloud_resource.managementID = ?", cloudResource.ManagementID)
	}
	if cloudResource.OwnerLarkID != "" {
		model = model.Where("cloud_resource.ownerLarkID = ?", cloudResource.OwnerLarkID)
	}
	if cloudResource.CloudInsID != "" {
		model = model.Where("cloud_resource.cloudInsID = ?", cloudResource.CloudInsID)
	}
	if cloudResource.ProviderID != 0 {
		model = model.Where("cloud_resource.providerID = ?", cloudResource.ProviderID)
	}
	if cloudResource.DepartLarkID != "" {
		model = model.Where("cloud_resource.departLarkID = ?", cloudResource.DepartLarkID)
	}
	if cloudResource.AuthType != 0 {
		model = model.Where("cloud_resource.authType = ?", cloudResource.AuthType)
	}
	if cloudResource.ClassType != "" {
		model = model.Where("cloud_resource.classType = ?", cloudResource.ClassType)
	}
	err := model.First(cloudResource).Error

	return cloudResource, dbNotFoundErrorWrap(err)
}

func (c *cloudResource) Delete(ctx context.Context, cloudResource *v1.CloudResource) error {
	model := c.db.Where("1=1")
	if cloudResource.ID != 0 {
		model = model.Where("id = ?", cloudResource.ID)
	}
	if cloudResource.Name != "" {
		model = model.Where("name = ?", cloudResource.Name)
	}
	if cloudResource.ManagementID != "" {
		model = model.Where("managementID = ?", cloudResource.ManagementID)
	}
	if cloudResource.OwnerLarkID != "" {
		model = model.Where("ownerLarkID = ?", cloudResource.OwnerLarkID)
	}
	if cloudResource.CloudInsID != "" {
		model = model.Where("cloudInsID = ?", cloudResource.CloudInsID)
	}
	if cloudResource.ProviderID != 0 {
		model = model.Where("providerID = ?", cloudResource.ProviderID)
	}
	if cloudResource.DepartLarkID != "" {
		model = model.Where("departLarkID = ?", cloudResource.DepartLarkID)
	}
	if cloudResource.ManagementType != 0 {
		model = model.Where("managementType = ?", cloudResource.ManagementType)
	}
	if cloudResource.AuthType != 0 {
		model = model.Where("authType = ?", cloudResource.AuthType)
	}
	if cloudResource.ClassType != "" {
		model = model.Where("classType = ?", cloudResource.ClassType)
	}
	err := model.Delete(&v1.CloudResource{}).Error

	return err
}

func (c *cloudResource) Search(ctx context.Context, searchInfo string) ([]*v1.CloudResource, error) {
	var res []*v1.CloudResource
	tDetail := "%" + searchInfo + "%"
	err := c.db.
		Model(&v1.CloudResource{}).
		Joins("Owner").
		Joins("Department").
		Joins("Provider").
		Where("managementType=1").
		Where("LOWER(cloud_resource.name) LIKE ? OR LOWER(cloud_resource.managementID) LIKE ? OR LOWER(cloud_resource.cloudInsID) LIKE ?", tDetail, tDetail, tDetail).
		Order("cloud_resource.id ASC").
		Limit(10).
		Scan(&res).Error

	return res, dbNotFoundErrorWrap(err)
}

func (c *cloudResource) ListByProvider(_ context.Context, provider string) ([]*v1.CloudResource, error) {
	var res []*v1.CloudResource
	err := c.db.Joins("Provider").Where("Provider.code = ? and ClassType != 'Prod'", provider).Find(&res).Error

	return res, dbNotFoundErrorWrap(err)
}

func (c *cloudResource) ListByProviderWithProd(_ context.Context, provider string) ([]*v1.CloudResource, error) {
	var res []*v1.CloudResource
	err := c.db.Joins("Provider").Where("Provider.code = ?", provider).Find(&res).Error

	return res, dbNotFoundErrorWrap(err)
}
