package sso

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/tidbcloud/tiinf/errs"

	"github.com/tidbcloud/account/internel/dao/entity"
	"gorm.io/gorm"
)

type OrgAuthMethodDaoIface interface {
	CreateOrgAuthMethod(ctx context.Context, tx *gorm.DB, method *entity.OrgAuthMethod) (*entity.OrgAuthMethod, error)
	UpdateOrgAuthMethod(ctx context.Context, tx *gorm.DB, method *entity.OrgAuthMethod) error
	ListOrgAuthMethodsByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) ([]entity.OrgAuthMethod, error)
	ListOrgAuthMethods(ctx context.Context, tx *gorm.DB, filter OrgAuthMethodFilter) ([]entity.OrgAuthMethod, error)
	GetOrgAuthMethodByName(ctx context.Context, tx *gorm.DB, orgID uint64, methodName string) (*entity.OrgAuthMethod, error)
	SetAuthMethodStatus(ctx context.Context, tx *gorm.DB, e *entity.OrgAuthMethod) error
}
type OrgAuthMethodDao struct {
}

func NewOrgAuthMethodDao() OrgAuthMethodDaoIface {
	return &OrgAuthMethodDao{}
}

type OrgAuthMethodFilter struct {
	OrgIds    []uint64
	Names     []string
	Types     []entity.AuthMethodType
	TargetIDs []entity.ID
	Status    []entity.EntityStatus
}

func (dao *OrgAuthMethodDao) CreateOrgAuthMethod(ctx context.Context, tx *gorm.DB, method *entity.OrgAuthMethod) (*entity.OrgAuthMethod, error) {
	err := tx.Model(&entity.OrgAuthMethod{}).Create(method).Error
	if err != nil {
		return nil, err
	}
	return method, nil

}

func (dao *OrgAuthMethodDao) UpdateOrgAuthMethod(ctx context.Context, tx *gorm.DB, e *entity.OrgAuthMethod) error {
	dbNameToFieldName := map[string]interface{}{}
	if e.UpdatedBy != "" {
		dbNameToFieldName["updated_by"] = e.UpdatedBy
	}
	if e.Status != "" {
		dbNameToFieldName["status"] = e.Status
	}

	if e.Name != "" {
		dbNameToFieldName["name"] = e.Name
	}
	if e.DisplayName != "" {
		dbNameToFieldName["display_name"] = e.DisplayName
	}

	dbNameToFieldName["allow_email_domain"] = e.AllowEmailDomain
	dbNameToFieldName["content"] = e.Content
	dbNameToFieldName["auto_provision"] = e.AutoProvision
	dbNameToFieldName["scim_provision"] = e.ScimProvision
	tx = tx.Model(&entity.OrgAuthMethod{})
	err := tx.Where("id=?", e.ID.Uint64()).Updates(dbNameToFieldName).Error
	if err != nil {
		return errs.WrapGORMError(err, fmt.Sprintf("UpdateOrgAuthMethod failed: %+v", e))
	}
	return nil
}
func (dao *OrgAuthMethodDao) SetAuthMethodStatus(ctx context.Context, tx *gorm.DB, e *entity.OrgAuthMethod) error {
	dbNameToFieldName := map[string]interface{}{}
	if e.UpdatedBy != "" {
		dbNameToFieldName["updated_by"] = e.UpdatedBy
	}
	if e.Status != "" {
		dbNameToFieldName["status"] = e.Status
	}

	tx = tx.Model(&entity.OrgAuthMethod{})
	err := tx.Where("org_id=?", e.OrgID.Uint64()).Where("name=?", e.Name).Updates(dbNameToFieldName).Error
	if err != nil {
		return errs.WrapGORMError(err, fmt.Sprintf("UpdateOrgAuthMethod failed: %+v", e))
	}
	return nil
}

func (dao *OrgAuthMethodDao) ListOrgAuthMethodsByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) ([]entity.OrgAuthMethod, error) {
	tx = tx.Model(&entity.OrgAuthMethod{})
	tx = tx.Where("org_id = ?", orgID)
	tx = tx.Order("created_at, id asc")

	var methods []entity.OrgAuthMethod
	if err := tx.Find(&methods).Error; err != nil {
		return nil, errs.WrapGORMError(err, fmt.Sprintf("ListOrgAuthMethodsByOrgID failed: %+v", orgID))
	}
	return methods, nil

}
func (dao *OrgAuthMethodDao) ListOrgAuthMethods(ctx context.Context, tx *gorm.DB, filter OrgAuthMethodFilter) ([]entity.OrgAuthMethod, error) {
	tx = tx.Model(&entity.OrgAuthMethod{})
	if len(filter.OrgIds) == 0 && len(filter.Names) == 0 && len(filter.Types) == 0 && len(filter.Status) > 0 {
		return nil, errs.NewBadRequest(" OrgAuthMethodFilter must not null", nil)
	}
	if len(filter.OrgIds) > 0 {
		tx = tx.Where("org_id in (?)", filter.OrgIds)
	}
	if len(filter.Names) > 0 {
		tx = tx.Where("name in (?)", filter.Names)
	}
	if len(filter.Types) > 0 {
		tx = tx.Where("type in (?)", filter.Types)
	}
	if len(filter.Status) > 0 {
		tx = tx.Where("status in (?)", filter.Status)
	}

	var methods []entity.OrgAuthMethod
	if err := tx.Find(&methods).Error; err != nil {

		return nil, errs.WrapGORMError(err, fmt.Sprintf("ListOrgAuthMethods failed: %+v", filter))
	}
	return methods, nil
}
func (dao *OrgAuthMethodDao) GetOrgAuthMethodByName(ctx context.Context, tx *gorm.DB, orgID uint64, methodName string) (*entity.OrgAuthMethod, error) {
	res := &entity.OrgAuthMethod{}
	tx = tx.Model(&entity.OrgAuthMethod{})
	err := tx.Where("org_id = ? and name = ? ", orgID, methodName).First(res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		tilog.Error(ctx, "dao GetOrgAuthMethodByName err", zap.Any("err", err))
		return nil, errs.WrapGORMError(err, fmt.Sprintf("GetOrgAuthMethodByOrgIDMethodName failed: orgID: %+v, methodName: %+v", orgID, methodName))
	}
	return res, nil
}
