package domain

import (
	"context"
	"errors"
	"fmt"
	"strings"

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

type OrgDomainDaoIface interface {
	Create(ctx context.Context, tx *gorm.DB, domain *entity.OrgDomain) (*entity.OrgDomain, error)
	Update(ctx context.Context, tx *gorm.DB, domain *entity.OrgDomain) error
	BatchUpdateByIDs(ctx context.Context, tx *gorm.DB, ids []uint64, fields map[string]interface{}) error
	GetByID(ctx context.Context, tx *gorm.DB, id uint64) (*entity.OrgDomain, error)
	GetByOrgAndDomain(ctx context.Context, tx *gorm.DB, orgID uint64, domainName string) (*entity.OrgDomain, error)
	ListByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) ([]entity.OrgDomain, error)
	ListByIDsAndOrgID(ctx context.Context, tx *gorm.DB, ids []uint64, orgID uint64) ([]entity.OrgDomain, error)
	Delete(ctx context.Context, tx *gorm.DB, id uint64) error
}

type OrgDomainDao struct{}

func NewOrgDomainDao() OrgDomainDaoIface {
	return &OrgDomainDao{}
}

func (dao *OrgDomainDao) Create(ctx context.Context, tx *gorm.DB, domain *entity.OrgDomain) (*entity.OrgDomain, error) {
	err := tx.Model(&entity.OrgDomain{}).Create(domain).Error
	if err != nil {
		return nil, fmt.Errorf("create org domain failed: %w", err)
	}
	return domain, nil
}

func (dao *OrgDomainDao) Update(ctx context.Context, tx *gorm.DB, d *entity.OrgDomain) error {
	fields := map[string]interface{}{}
	if d.Status != "" {
		fields["status"] = d.Status
	}
	if d.VerificationToken != "" {
		fields["verification_token"] = d.VerificationToken
	}
	if d.VerifiedAt != nil {
		fields["verified_at"] = d.VerifiedAt
	}
	if d.ConfirmedAt != nil {
		fields["confirmed_at"] = d.ConfirmedAt
	}
	if d.ConfirmedBy != "" {
		fields["confirmed_by"] = d.ConfirmedBy
	}
	if d.ExpiresAt != nil {
		fields["expires_at"] = d.ExpiresAt
	}
	err := tx.Model(&entity.OrgDomain{}).Where("id = ?", d.ID.Uint64()).Updates(fields).Error
	if err != nil {
		return fmt.Errorf("update org domain failed: %w", err)
	}
	return nil
}

func (dao *OrgDomainDao) GetByID(ctx context.Context, tx *gorm.DB, id uint64) (*entity.OrgDomain, error) {
	var d entity.OrgDomain
	err := tx.Model(&entity.OrgDomain{}).Where("id = ?", id).First(&d).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, fmt.Errorf("get org domain by id failed: %w", err)
	}
	return &d, nil
}

func (dao *OrgDomainDao) GetByOrgAndDomain(ctx context.Context, tx *gorm.DB, orgID uint64, domainName string) (*entity.OrgDomain, error) {
	var d entity.OrgDomain
	err := tx.Model(&entity.OrgDomain{}).
		Where("org_id = ? AND domain_name = ?", orgID, strings.ToLower(domainName)).
		First(&d).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, fmt.Errorf("get org domain by org and domain failed: %w", err)
	}
	return &d, nil
}

func (dao *OrgDomainDao) ListByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) ([]entity.OrgDomain, error) {
	var domains []entity.OrgDomain
	err := tx.Model(&entity.OrgDomain{}).
		Where("org_id = ?", orgID).
		Order("created_at ASC").
		Find(&domains).Error
	if err != nil {
		return nil, fmt.Errorf("list org domains failed: %w", err)
	}
	return domains, nil
}

func (dao *OrgDomainDao) ListByIDsAndOrgID(ctx context.Context, tx *gorm.DB, ids []uint64, orgID uint64) ([]entity.OrgDomain, error) {
	if len(ids) == 0 {
		return nil, nil
	}
	var domains []entity.OrgDomain
	err := tx.Model(&entity.OrgDomain{}).
		Where("id IN ? AND org_id = ?", ids, orgID).
		Find(&domains).Error
	if err != nil {
		return nil, fmt.Errorf("list org domains by ids and org_id failed: %w", err)
	}
	return domains, nil
}

func (dao *OrgDomainDao) BatchUpdateByIDs(ctx context.Context, tx *gorm.DB, ids []uint64, fields map[string]interface{}) error {
	if len(ids) == 0 || len(fields) == 0 {
		return nil
	}
	err := tx.Model(&entity.OrgDomain{}).Where("id IN ?", ids).Updates(fields).Error
	if err != nil {
		return fmt.Errorf("batch update org domains failed: %w", err)
	}
	return nil
}

func (dao *OrgDomainDao) Delete(ctx context.Context, tx *gorm.DB, id uint64) error {
	err := tx.Model(&entity.OrgDomain{}).Where("id = ?", id).Delete(&entity.OrgDomain{}).Error
	if err != nil {
		return fmt.Errorf("delete org domain failed: %w", err)
	}
	return nil
}
