package handler

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

	"github.com/google/uuid"
)

func representativeTenantIDFromTraits(traits map[string]any) string {
	if value := tenantClaimString(traits, "tenant_id"); value != "" {
		return value
	}
	if value := tenantClaimString(traits, "primaryTenantId"); value != "" {
		return value
	}
	if metadata, ok := traits["metadata"].(map[string]any); ok {
		if value := tenantClaimString(metadata, "primaryTenantId"); value != "" {
			return value
		}
	}

	appointments := tenantAssignmentAppointmentsFromTraits(traits)
	for _, appointment := range appointments {
		if tenantAssignmentBool(appointment, "isPrimary", "primary", "representative", "isRepresentative") {
			if value := tenantAssignmentTenantID(appointment); value != "" {
				return value
			}
		}
	}
	for _, appointment := range appointments {
		if value := tenantAssignmentTenantID(appointment); value != "" {
			return value
		}
	}

	for _, tenantID := range tenantNamespaceIDsFromTraits(traits) {
		return tenantID
	}
	return ""
}

func joinedTenantIDsFromTraits(traits map[string]any, representativeTenantID string) []string {
	values := make([]string, 0)
	if representativeTenantID != "" {
		values = append(values, representativeTenantID)
	}
	if value := tenantClaimString(traits, "tenant_id"); value != "" {
		values = append(values, value)
	}
	for _, appointment := range tenantAssignmentAppointmentsFromTraits(traits) {
		if value := tenantAssignmentTenantID(appointment); value != "" {
			values = append(values, value)
		}
	}
	values = append(values, tenantNamespaceIDsFromTraits(traits)...)
	return uniqueSortedStrings(values)
}

func tenantAssignmentAppointmentsFromTraits(traits map[string]any) []map[string]any {
	raw := rawAdditionalAppointments(traits)
	switch values := raw.(type) {
	case []any:
		appointments := make([]map[string]any, 0, len(values))
		for _, item := range values {
			if appointment, ok := item.(map[string]any); ok {
				appointments = append(appointments, appointment)
			}
		}
		return appointments
	case []map[string]any:
		return values
	default:
		return nil
	}
}

func tenantAssignmentTenantID(appointment map[string]any) string {
	for _, key := range []string{"tenantId", "tenant_id"} {
		if value := tenantClaimString(appointment, key); value != "" {
			return value
		}
	}
	return ""
}

func tenantAssignmentBool(values map[string]any, keys ...string) bool {
	for _, key := range keys {
		raw, ok := values[key]
		if !ok || raw == nil {
			continue
		}
		switch value := raw.(type) {
		case bool:
			if value {
				return true
			}
		case string:
			normalized := strings.ToLower(strings.TrimSpace(value))
			if normalized == "true" || normalized == "1" || normalized == "yes" {
				return true
			}
		}
	}
	return false
}

func tenantNamespaceIDsFromTraits(traits map[string]any) []string {
	if traits == nil {
		return nil
	}
	ids := make([]string, 0)
	for key, value := range traits {
		if key == "" || key == "metadata" {
			continue
		}
		switch value.(type) {
		case map[string]any:
			ids = append(ids, key)
		}
	}
	return uniqueSortedStrings(ids)
}

func createPersonalTenantForUser(ctx context.Context, tenantService service.TenantService, email string) (*domain.Tenant, error) {
	if tenantService == nil {
		return nil, errors.New("tenant service unavailable")
	}
	normalizedEmail := strings.ToLower(strings.TrimSpace(email))
	if normalizedEmail == "" {
		normalizedEmail = "user"
	}
	slug := "personal-" + strings.ReplaceAll(uuid.NewString(), "-", "")
	tenant, err := tenantService.RegisterTenant(
		ctx,
		fmt.Sprintf("Personal - %s", normalizedEmail),
		slug,
		domain.TenantTypePersonal,
		"Automatically provisioned personal tenant",
		nil,
		nil,
		"",
	)
	if err != nil {
		return nil, err
	}
	if tenant == nil {
		return nil, errors.New("personal tenant not created")
	}
	return tenant, nil
}
