package handler

import (
	"baron-sso-backend/internal/domain"
	"context"
	"fmt"
	"strings"
)

const hanmacFamilyTenantSlug = "hanmac-family"

type hanmacEmailScope struct {
	TenantIDs map[string]bool
	Slugs     map[string]bool
	IDList    []string
	SlugList  []string
}

type hanmacEmailEvaluation struct {
	Email          string
	OriginalEmail  string
	SuggestedEmail string
	Status         string
	Warnings       []string
	Message        string
	Blocking       bool
	LocalPart      string
}

func (h *UserHandler) evaluateHanmacImportEmail(ctx context.Context, item bulkUserItem, scope *hanmacEmailScope, usedLocalParts map[string]bool) hanmacEmailEvaluation {
	originalEmail := strings.TrimSpace(item.Email)
	name := strings.TrimSpace(item.Name)
	evaluation := hanmacEmailEvaluation{
		Email:         originalEmail,
		OriginalEmail: originalEmail,
		Status:        "valid",
	}

	localPart, domainPart, err := domain.SplitEmailDomain(originalEmail)
	if err != nil {
		evaluation.Status = "blockingError"
		evaluation.Message = "invalid email format"
		evaluation.Blocking = true
		return evaluation
	}

	base, needsReview, _ := domain.BuildKoreanNameEmailBase(name)
	if needsReview {
		evaluation.Warnings = append(evaluation.Warnings, "needsReview")
		evaluation.Status = "needsReview"
	}

	if localPart == "" {
		if base == "" {
			evaluation.Status = "blockingError"
			evaluation.Message = "이름으로 이메일 ID를 제안할 수 없습니다."
			evaluation.Blocking = true
			return evaluation
		}
		nextLocalPart := nextAvailableHanmacLocalPart(base, usedLocalParts)
		evaluation.Email = nextLocalPart + "@" + domainPart
		evaluation.SuggestedEmail = evaluation.Email
		evaluation.LocalPart = nextLocalPart
		evaluation.Status = "suggested"
		evaluation.Warnings = appendUniqueString(evaluation.Warnings, "suggested")
		return evaluation
	}

	evaluation.LocalPart = localPart
	if usedLocalParts[localPart] {
		evaluation.Status = "blockingError"
		evaluation.Message = "한맥가족 내에서 이미 사용 중인 이메일 ID입니다."
		evaluation.Blocking = true
		return evaluation
	}

	if base != "" && !domain.MatchesSuggestedNameRule(localPart, base) {
		evaluation.Status = "ruleMismatch"
		evaluation.Warnings = appendUniqueString(evaluation.Warnings, "ruleMismatch")
	}

	if evaluation.Status == "needsReview" && len(evaluation.Warnings) == 0 {
		evaluation.Warnings = append(evaluation.Warnings, "needsReview")
	}
	_ = scope
	return evaluation
}

func (h *UserHandler) ensureHanmacCreateEmailAllowed(ctx context.Context, email string, tenantSlug string, tenantID string) error {
	scope, err := h.resolveHanmacEmailScope(ctx)
	if err != nil || scope == nil || !scope.ContainsTenant(tenantID, tenantSlug) {
		return nil
	}

	localPart, err := domain.ExtractNormalizedEmailLocalPart(email)
	if err != nil {
		return err
	}

	usedLocalParts, err := h.loadHanmacLocalParts(ctx, scope)
	if err != nil {
		return err
	}
	if usedLocalParts[localPart] {
		return fmt.Errorf("한맥가족 내에서 이미 사용 중인 이메일 ID입니다.")
	}
	return nil
}

func (h *UserHandler) resolveHanmacEmailScope(ctx context.Context) (*hanmacEmailScope, error) {
	if h.TenantService == nil {
		return nil, nil
	}

	tenants, _, err := h.TenantService.ListTenants(ctx, 10000, 0, "")
	if err != nil {
		return nil, err
	}

	var rootID string
	for _, tenant := range tenants {
		if strings.EqualFold(strings.TrimSpace(tenant.Slug), hanmacFamilyTenantSlug) {
			rootID = tenant.ID
			break
		}
	}
	if rootID == "" {
		return nil, nil
	}

	tenantByID := make(map[string]domain.Tenant, len(tenants))
	for _, tenant := range tenants {
		tenantByID[tenant.ID] = tenant
	}

	scope := &hanmacEmailScope{
		TenantIDs: make(map[string]bool),
		Slugs:     make(map[string]bool),
	}
	for _, tenant := range tenants {
		if isTenantDescendantOf(tenant, rootID, tenantByID) {
			scope.TenantIDs[tenant.ID] = true
			scope.Slugs[strings.ToLower(strings.TrimSpace(tenant.Slug))] = true
			scope.IDList = append(scope.IDList, tenant.ID)
			scope.SlugList = append(scope.SlugList, tenant.Slug)
		}
	}
	return scope, nil
}

func (h *UserHandler) loadHanmacLocalParts(ctx context.Context, scope *hanmacEmailScope) (map[string]bool, error) {
	used := make(map[string]bool)
	if h.UserRepo == nil || scope == nil {
		return used, nil
	}

	if len(scope.IDList) > 0 {
		users, err := h.UserRepo.FindByTenantIDs(ctx, scope.IDList)
		if err != nil {
			return nil, err
		}
		addUserEmailLocalParts(used, users)
	}

	if len(scope.SlugList) > 0 {
		users, err := h.UserRepo.FindByCompanyCodes(ctx, scope.SlugList)
		if err != nil {
			return nil, err
		}
		addUserEmailLocalParts(used, users)
	}

	return used, nil
}

func (s *hanmacEmailScope) ContainsTenant(tenantID string, slug string) bool {
	if s == nil {
		return false
	}
	if tenantID != "" && s.TenantIDs[tenantID] {
		return true
	}
	return s.Slugs[strings.ToLower(strings.TrimSpace(slug))]
}

func isTenantDescendantOf(tenant domain.Tenant, rootID string, tenantByID map[string]domain.Tenant) bool {
	if tenant.ID == rootID {
		return true
	}
	visited := make(map[string]bool)
	parentID := ""
	if tenant.ParentID != nil {
		parentID = *tenant.ParentID
	}
	for parentID != "" {
		if parentID == rootID {
			return true
		}
		if visited[parentID] {
			return false
		}
		visited[parentID] = true
		parent, ok := tenantByID[parentID]
		if !ok || parent.ParentID == nil {
			return false
		}
		parentID = *parent.ParentID
	}
	return false
}

func addUserEmailLocalParts(target map[string]bool, users []domain.User) {
	for _, user := range users {
		localPart, err := domain.ExtractNormalizedEmailLocalPart(user.Email)
		if err == nil && localPart != "" {
			target[localPart] = true
		}
	}
}

func nextAvailableHanmacLocalPart(base string, usedLocalParts map[string]bool) string {
	base = strings.ToLower(strings.TrimSpace(base))
	if base == "" {
		return ""
	}
	if !usedLocalParts[base] {
		return base
	}
	for index := 1; ; index++ {
		candidate := fmt.Sprintf("%s%d", base, index)
		if !usedLocalParts[candidate] {
			return candidate
		}
	}
}

func appendUniqueString(values []string, value string) []string {
	for _, existing := range values {
		if existing == value {
			return values
		}
	}
	return append(values, value)
}
