package usecase

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libkg "haixun-backend/internal/library/knowledge"
	"haixun-backend/internal/library/placement"
	"haixun-backend/internal/library/websearch"
	settingdomain "haixun-backend/internal/model/setting/domain/usecase"
)

const (
	settingScopeUser = "user"
	keyResearch      = "placement.research"
)

type Settings struct {
	WebSearchProvider     string
	BraveAPIKey           string
	BraveAPIKeyConfigured bool
	ExaAPIKey             string
	ExaAPIKeyConfigured   bool
	BraveCountry          string
	BraveSearchLang       string
	ExaUserLocation       string
	ExpandStrategy        string
}

type SettingsPatch struct {
	WebSearchProvider *string
	BraveAPIKey       *string
	ExaAPIKey         *string
	BraveCountry      *string
	BraveSearchLang   *string
	ExaUserLocation   *string
	ExpandStrategy    *string
}

type UseCase interface {
	Get(ctx context.Context, tenantID, ownerUID string) (*Settings, error)
	Update(ctx context.Context, tenantID, ownerUID string, patch SettingsPatch) (*Settings, error)
	ResearchSettings(ctx context.Context, tenantID, ownerUID string) (placement.ResearchSettings, error)
}

type placementUseCase struct {
	settings settingdomain.UseCase
}

func NewUseCase(settings settingdomain.UseCase) UseCase {
	return &placementUseCase{settings: settings}
}

func (u *placementUseCase) Get(ctx context.Context, tenantID, ownerUID string) (*Settings, error) {
	if err := requireActor(tenantID, ownerUID); err != nil {
		return nil, err
	}
	stored, err := u.load(ctx, ownerUID)
	if err != nil {
		return nil, err
	}
	return toPublic(stored), nil
}

func (u *placementUseCase) Update(ctx context.Context, tenantID, ownerUID string, patch SettingsPatch) (*Settings, error) {
	if err := requireActor(tenantID, ownerUID); err != nil {
		return nil, err
	}
	current, err := u.load(ctx, ownerUID)
	if err != nil {
		return nil, err
	}
	next := applyPatch(current, patch)
	if err := u.save(ctx, ownerUID, next); err != nil {
		return nil, err
	}
	return toPublic(next), nil
}

func (u *placementUseCase) ResearchSettings(ctx context.Context, tenantID, ownerUID string) (placement.ResearchSettings, error) {
	if err := requireActor(tenantID, ownerUID); err != nil {
		return placement.ResearchSettings{}, err
	}
	stored, err := u.load(ctx, ownerUID)
	if err != nil {
		return placement.ResearchSettings{}, err
	}
	return placement.ResearchSettings{
		WebSearchProvider: stored.WebSearchProvider,
		BraveAPIKey:       stored.BraveAPIKey,
		ExaAPIKey:         stored.ExaAPIKey,
		BraveCountry:      stored.BraveCountry,
		BraveSearchLang:   stored.BraveSearchLang,
		ExaUserLocation:   stored.ExaUserLocation,
		ExpandStrategy:    stored.ExpandStrategy,
	}, nil
}

func (u *placementUseCase) load(ctx context.Context, ownerUID string) (storedSettings, error) {
	defaults := defaultSettings()
	setting, err := u.settings.Get(ctx, settingScopeUser, ownerUID, keyResearch)
	if err != nil {
		if isSettingNotFound(err) {
			return defaults, nil
		}
		return defaults, err
	}
	return mergeSettings(defaults, setting.Value), nil
}

func (u *placementUseCase) save(ctx context.Context, ownerUID string, value storedSettings) error {
	_, err := u.settings.Upsert(ctx, settingdomain.UpsertRequest{
		Scope:   settingScopeUser,
		ScopeID: ownerUID,
		Key:     keyResearch,
		Value:   value.toMap(),
	})
	return err
}

type storedSettings struct {
	WebSearchProvider string
	BraveAPIKey       string
	ExaAPIKey         string
	BraveCountry      string
	BraveSearchLang   string
	ExaUserLocation   string
	ExpandStrategy    string
}

func defaultSettings() storedSettings {
	return storedSettings{
		WebSearchProvider: string(websearch.ProviderBrave),
		BraveCountry:      "tw",
		BraveSearchLang:   "zh-hant",
		ExaUserLocation:   "TW",
		ExpandStrategy:    string(libkg.ExpandStrategyHybrid),
	}
}

func mergeSettings(defaults storedSettings, raw map[string]interface{}) storedSettings {
	if raw == nil {
		return defaults
	}
	if v, ok := raw["web_search_provider"].(string); ok && strings.TrimSpace(v) != "" {
		defaults.WebSearchProvider = string(websearch.ParseProvider(v))
	}
	if v, ok := raw["brave_api_key"].(string); ok {
		defaults.BraveAPIKey = strings.TrimSpace(v)
	}
	if v, ok := raw["exa_api_key"].(string); ok {
		defaults.ExaAPIKey = strings.TrimSpace(v)
	}
	if v, ok := raw["brave_country"].(string); ok && strings.TrimSpace(v) != "" {
		defaults.BraveCountry = strings.TrimSpace(v)
	}
	if v, ok := raw["brave_search_lang"].(string); ok && strings.TrimSpace(v) != "" {
		defaults.BraveSearchLang = strings.TrimSpace(v)
	}
	if v, ok := raw["exa_user_location"].(string); ok && strings.TrimSpace(v) != "" {
		defaults.ExaUserLocation = strings.TrimSpace(v)
	}
	if v, ok := raw["expand_strategy"].(string); ok && strings.TrimSpace(v) != "" {
		defaults.ExpandStrategy = string(libkg.ParseExpandStrategy(v))
	}
	return defaults
}

func applyPatch(current storedSettings, patch SettingsPatch) storedSettings {
	if patch.WebSearchProvider != nil && strings.TrimSpace(*patch.WebSearchProvider) != "" {
		current.WebSearchProvider = string(websearch.ParseProvider(*patch.WebSearchProvider))
	}
	if patch.BraveAPIKey != nil {
		value := strings.TrimSpace(*patch.BraveAPIKey)
		if value != "" && !isMaskedAPIKey(value) {
			current.BraveAPIKey = value
		}
	}
	if patch.ExaAPIKey != nil {
		value := strings.TrimSpace(*patch.ExaAPIKey)
		if value != "" && !isMaskedAPIKey(value) {
			current.ExaAPIKey = value
		}
	}
	if patch.BraveCountry != nil && strings.TrimSpace(*patch.BraveCountry) != "" {
		current.BraveCountry = strings.TrimSpace(*patch.BraveCountry)
	}
	if patch.BraveSearchLang != nil && strings.TrimSpace(*patch.BraveSearchLang) != "" {
		current.BraveSearchLang = strings.TrimSpace(*patch.BraveSearchLang)
	}
	if patch.ExaUserLocation != nil && strings.TrimSpace(*patch.ExaUserLocation) != "" {
		current.ExaUserLocation = strings.TrimSpace(*patch.ExaUserLocation)
	}
	if patch.ExpandStrategy != nil && strings.TrimSpace(*patch.ExpandStrategy) != "" {
		current.ExpandStrategy = string(libkg.ParseExpandStrategy(*patch.ExpandStrategy))
	}
	return current
}

func (s storedSettings) toMap() map[string]interface{} {
	return map[string]interface{}{
		"web_search_provider": s.WebSearchProvider,
		"brave_api_key":       s.BraveAPIKey,
		"exa_api_key":         s.ExaAPIKey,
		"brave_country":       s.BraveCountry,
		"brave_search_lang":   s.BraveSearchLang,
		"exa_user_location":   s.ExaUserLocation,
		"expand_strategy":     s.ExpandStrategy,
	}
}

func toPublic(stored storedSettings) *Settings {
	braveMasked := ""
	if stored.BraveAPIKey != "" {
		braveMasked = maskAPIKey(stored.BraveAPIKey)
	}
	exaMasked := ""
	if stored.ExaAPIKey != "" {
		exaMasked = maskAPIKey(stored.ExaAPIKey)
	}
	strategy := string(libkg.ParseExpandStrategy(stored.ExpandStrategy))
	return &Settings{
		WebSearchProvider:     string(websearch.ParseProvider(stored.WebSearchProvider)),
		BraveAPIKey:           braveMasked,
		BraveAPIKeyConfigured: strings.TrimSpace(stored.BraveAPIKey) != "",
		ExaAPIKey:             exaMasked,
		ExaAPIKeyConfigured:   strings.TrimSpace(stored.ExaAPIKey) != "",
		BraveCountry:          stored.BraveCountry,
		BraveSearchLang:       stored.BraveSearchLang,
		ExaUserLocation:       stored.ExaUserLocation,
		ExpandStrategy:        strategy,
	}
}

func requireActor(tenantID, ownerUID string) error {
	if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
		return app.For(code.Member).InputMissingRequired("tenant_id and uid are required")
	}
	return nil
}

func isSettingNotFound(err error) bool {
	if err == nil {
		return false
	}
	appErr := app.FromError(err)
	return appErr != nil && strings.Contains(strings.ToLower(appErr.Error()), "not found")
}

func maskAPIKey(key string) string {
	trimmed := strings.TrimSpace(key)
	if trimmed == "" {
		return ""
	}
	if len(trimmed) <= 4 {
		return "••••"
	}
	return "••••" + trimmed[len(trimmed)-4:]
}

func isMaskedAPIKey(value string) bool {
	return strings.HasPrefix(value, "••••")
}
