/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package siteinfo

import (
	"context"
	"encoding/json"
	errpkg "errors"
	"fmt"
	"strings"

	"github.com/apache/answer/internal/base/constant"
	"github.com/apache/answer/internal/base/handler"
	"github.com/apache/answer/internal/base/reason"
	"github.com/apache/answer/internal/base/translator"
	"github.com/apache/answer/internal/entity"
	"github.com/apache/answer/internal/schema"
	"github.com/apache/answer/internal/service/config"
	"github.com/apache/answer/internal/service/export"
	"github.com/apache/answer/internal/service/file_record"
	questioncommon "github.com/apache/answer/internal/service/question_common"
	"github.com/apache/answer/internal/service/siteinfo_common"
	tagcommon "github.com/apache/answer/internal/service/tag_common"
	"github.com/apache/answer/plugin"
	"github.com/go-resty/resty/v2"
	"github.com/jinzhu/copier"
	"github.com/segmentfault/pacman/errors"
	"github.com/segmentfault/pacman/log"
)

type SiteInfoService struct {
	siteInfoRepo          siteinfo_common.SiteInfoRepo
	siteInfoCommonService siteinfo_common.SiteInfoCommonService
	emailService          *export.EmailService
	tagCommonService      *tagcommon.TagCommonService
	configService         *config.ConfigService
	questioncommon        *questioncommon.QuestionCommon
	fileRecordService     *file_record.FileRecordService
}

func NewSiteInfoService(
	siteInfoRepo siteinfo_common.SiteInfoRepo,
	siteInfoCommonService siteinfo_common.SiteInfoCommonService,
	emailService *export.EmailService,
	tagCommonService *tagcommon.TagCommonService,
	configService *config.ConfigService,
	questioncommon *questioncommon.QuestionCommon,
	fileRecordService *file_record.FileRecordService,

) *SiteInfoService {
	plugin.RegisterGetSiteURLFunc(func() string {
		generalSiteInfo, err := siteInfoCommonService.GetSiteGeneral(context.Background())
		if err != nil {
			log.Error(err)
			return ""
		}
		return generalSiteInfo.SiteUrl
	})

	return &SiteInfoService{
		siteInfoRepo:          siteInfoRepo,
		siteInfoCommonService: siteInfoCommonService,
		emailService:          emailService,
		tagCommonService:      tagCommonService,
		configService:         configService,
		questioncommon:        questioncommon,
		fileRecordService:     fileRecordService,
	}
}

// GetSiteGeneral get site info general
func (s *SiteInfoService) GetSiteGeneral(ctx context.Context) (resp *schema.SiteGeneralResp, err error) {
	return s.siteInfoCommonService.GetSiteGeneral(ctx)
}

// GetSiteInterface get site info interface
func (s *SiteInfoService) GetSiteInterface(ctx context.Context) (resp *schema.SiteInterfaceSettingsResp, err error) {
	return s.siteInfoCommonService.GetSiteInterface(ctx)
}

// GetSiteUsersSettings get site info users settings
func (s *SiteInfoService) GetSiteUsersSettings(ctx context.Context) (resp *schema.SiteUsersSettingsResp, err error) {
	return s.siteInfoCommonService.GetSiteUsersSettings(ctx)
}

// GetSiteBranding get site info branding
func (s *SiteInfoService) GetSiteBranding(ctx context.Context) (resp *schema.SiteBrandingResp, err error) {
	return s.siteInfoCommonService.GetSiteBranding(ctx)
}

// GetSiteUsers get site info about users
func (s *SiteInfoService) GetSiteUsers(ctx context.Context) (resp *schema.SiteUsersResp, err error) {
	return s.siteInfoCommonService.GetSiteUsers(ctx)
}

// GetSiteTag get site info write
func (s *SiteInfoService) GetSiteTag(ctx context.Context) (resp *schema.SiteTagsResp, err error) {
	resp, err = s.siteInfoCommonService.GetSiteTag(ctx)
	if err != nil {
		log.Error(err)
		return resp, nil
	}

	resp.RecommendTags, err = s.tagCommonService.GetSiteWriteRecommendTag(ctx)
	if err != nil {
		log.Error(err)
	}
	resp.ReservedTags, err = s.tagCommonService.GetSiteWriteReservedTag(ctx)
	if err != nil {
		log.Error(err)
	}
	return resp, nil
}

// GetSiteQuestion get site questions settings
func (s *SiteInfoService) GetSiteQuestion(ctx context.Context) (resp *schema.SiteQuestionsResp, err error) {
	return s.siteInfoCommonService.GetSiteQuestion(ctx)
}

// GetSiteAdvanced get site advanced settings
func (s *SiteInfoService) GetSiteAdvanced(ctx context.Context) (resp *schema.SiteAdvancedResp, err error) {
	return s.siteInfoCommonService.GetSiteAdvanced(ctx)
}

// GetSitePolicies get site legal info
func (s *SiteInfoService) GetSitePolicies(ctx context.Context) (resp *schema.SitePoliciesResp, err error) {
	return s.siteInfoCommonService.GetSitePolicies(ctx)
}

// GetSiteSecurity get site security info
func (s *SiteInfoService) GetSiteSecurity(ctx context.Context) (resp *schema.SiteSecurityResp, err error) {
	return s.siteInfoCommonService.GetSiteSecurity(ctx)
}

// GetSiteLogin get site login info
func (s *SiteInfoService) GetSiteLogin(ctx context.Context) (resp *schema.SiteLoginResp, err error) {
	return s.siteInfoCommonService.GetSiteLogin(ctx)
}

// GetSiteCustomCssHTML get site custom css html config
func (s *SiteInfoService) GetSiteCustomCssHTML(ctx context.Context) (resp *schema.SiteCustomCssHTMLResp, err error) {
	return s.siteInfoCommonService.GetSiteCustomCssHTML(ctx)
}

// GetSiteTheme get site theme config
func (s *SiteInfoService) GetSiteTheme(ctx context.Context) (resp *schema.SiteThemeResp, err error) {
	return s.siteInfoCommonService.GetSiteTheme(ctx)
}

func (s *SiteInfoService) SaveSiteGeneral(ctx context.Context, req schema.SiteGeneralReq) (err error) {
	req.FormatSiteUrl()
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeGeneral,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeGeneral, data)
}

func (s *SiteInfoService) SaveSiteInterface(ctx context.Context, req schema.SiteInterfaceReq) (err error) {
	// If the language is invalid, set it to the default language "en_US"
	if !translator.CheckLanguageIsValid(req.Language) {
		req.Language = "en_US"
	}

	content, _ := json.Marshal(req)
	data := entity.SiteInfo{
		Type:    constant.SiteTypeInterfaceSettings,
		Content: string(content),
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeInterfaceSettings, &data)
}

// SaveSiteUsersSettings save site users settings
func (s *SiteInfoService) SaveSiteUsersSettings(ctx context.Context, req schema.SiteUsersSettingsReq) (err error) {
	content, _ := json.Marshal(req)
	data := entity.SiteInfo{
		Type:    constant.SiteTypeInterfaceSettings,
		Content: string(content),
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeUsersSettings, &data)
}

// SaveSiteBranding save site branding information
func (s *SiteInfoService) SaveSiteBranding(ctx context.Context, req *schema.SiteBrandingReq) (err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeBranding,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeBranding, data)
}

// SaveSiteAdvanced save site advanced configuration
func (s *SiteInfoService) SaveSiteAdvanced(ctx context.Context, req *schema.SiteAdvancedReq) (resp any, err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeAdvanced,
		Content: string(content),
		Status:  1,
	}
	return nil, s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeAdvanced, data)
}

// SaveSiteQuestions save site questions configuration
func (s *SiteInfoService) SaveSiteQuestions(ctx context.Context, req *schema.SiteQuestionsReq) (resp any, err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeQuestions,
		Content: string(content),
		Status:  1,
	}
	return nil, s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeQuestions, data)
}

// SaveSiteTags save site tags configuration
func (s *SiteInfoService) SaveSiteTags(ctx context.Context, req *schema.SiteTagsReq) (resp any, err error) {
	recommendTags, reservedTags := make([]string, 0), make([]string, 0)
	recommendTagMapping, reservedTagMapping := make(map[string]bool), make(map[string]bool)
	for _, tag := range req.ReservedTags {
		if !recommendTagMapping[tag.SlugName] {
			reservedTagMapping[tag.SlugName] = true
			reservedTags = append(reservedTags, tag.SlugName)
		}
	}

	// recommend tags can't contain reserved tag
	for _, tag := range req.RecommendTags {
		if reservedTagMapping[tag.SlugName] {
			continue
		}
		if !recommendTagMapping[tag.SlugName] {
			recommendTagMapping[tag.SlugName] = true
			recommendTags = append(recommendTags, tag.SlugName)
		}
	}
	errData, err := s.tagCommonService.SetSiteWriteTag(ctx, recommendTags, reservedTags, req.UserID)
	if err != nil {
		return errData, err
	}

	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeTags,
		Content: string(content),
		Status:  1,
	}
	return nil, s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeTags, data)
}

// SaveSitePolicies save site policies configuration
func (s *SiteInfoService) SaveSitePolicies(ctx context.Context, req *schema.SitePoliciesReq) (err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypePolicies,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypePolicies, data)
}

// SaveSiteSecurity save site security configuration
func (s *SiteInfoService) SaveSiteSecurity(ctx context.Context, req *schema.SiteSecurityReq) (err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeSecurity,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeSecurity, data)
}

// SaveSiteLogin save site legal configuration
func (s *SiteInfoService) SaveSiteLogin(ctx context.Context, req *schema.SiteLoginReq) (err error) {
	if req.RequireEmailVerification == nil {
		return errors.BadRequest(reason.RequestFormatError)
	}

	loginConfig := &schema.SiteLoginResp{
		AllowNewRegistrations:    req.AllowNewRegistrations,
		AllowEmailRegistrations:  req.AllowEmailRegistrations,
		AllowPasswordLogin:       req.AllowPasswordLogin,
		AllowEmailDomains:        req.AllowEmailDomains,
		RequireEmailVerification: *req.RequireEmailVerification,
	}
	content, _ := json.Marshal(loginConfig)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeLogin,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeLogin, data)
}

// SaveSiteCustomCssHTML save site custom html configuration
func (s *SiteInfoService) SaveSiteCustomCssHTML(ctx context.Context, req *schema.SiteCustomCssHTMLReq) (err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeCustomCssHTML,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeCustomCssHTML, data)
}

// SaveSiteTheme save site custom html configuration
func (s *SiteInfoService) SaveSiteTheme(ctx context.Context, req *schema.SiteThemeReq) (err error) {
	if len(req.Layout) == 0 {
		req.Layout = constant.ThemeLayoutFullWidth
	}
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeTheme,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeTheme, data)
}

// SaveSiteUsers save site users
func (s *SiteInfoService) SaveSiteUsers(ctx context.Context, req *schema.SiteUsersReq) (err error) {
	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypeUsers,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeUsers, data)
}

// GetSiteAI get site AI configuration
func (s *SiteInfoService) GetSiteAI(ctx context.Context) (resp *schema.SiteAIResp, err error) {
	resp, err = s.siteInfoCommonService.GetSiteAI(ctx)
	if err != nil {
		return nil, err
	}
	aiProvider, err := s.GetAIProvider(ctx)
	if err != nil {
		return nil, err
	}
	providerMapping := make(map[string]*schema.SiteAIProvider)
	for _, provider := range resp.SiteAIProviders {
		providerMapping[provider.Provider] = provider
	}
	providers := make([]*schema.SiteAIProvider, 0)
	for _, p := range aiProvider {
		if provider, ok := providerMapping[p.Name]; ok {
			providers = append(providers, provider)
		} else {
			providers = append(providers, &schema.SiteAIProvider{
				Provider: p.Name,
			})
		}
	}
	resp.SiteAIProviders = providers
	s.maskAIKeys(resp)
	return resp, nil
}

// SaveSiteAI save site AI configuration
func (s *SiteInfoService) SaveSiteAI(ctx context.Context, req *schema.SiteAIReq) (err error) {
	if err := s.restoreMaskedAIKeys(ctx, req); err != nil {
		return err
	}
	if req.PromptConfig == nil {
		req.PromptConfig = &schema.AIPromptConfig{
			ZhCN: constant.DefaultAIPromptConfigZhCN,
			EnUS: constant.DefaultAIPromptConfigEnUS,
		}
	}

	aiProvider, err := s.GetAIProvider(ctx)
	if err != nil {
		return err
	}

	providerMapping := make(map[string]*schema.SiteAIProvider)
	for _, provider := range req.SiteAIProviders {
		providerMapping[provider.Provider] = provider
	}

	providers := make([]*schema.SiteAIProvider, 0)
	for _, p := range aiProvider {
		if provider, ok := providerMapping[p.Name]; ok {
			if len(provider.APIHost) == 0 && provider.Provider == req.ChosenProvider {
				provider.APIHost = p.DefaultAPIHost
			}
			providers = append(providers, provider)
		} else {
			providers = append(providers, &schema.SiteAIProvider{
				Provider: p.Name,
				APIHost:  p.DefaultAPIHost,
			})
		}
	}
	req.SiteAIProviders = providers

	content, _ := json.Marshal(req)
	siteInfo := &entity.SiteInfo{
		Type:    constant.SiteTypeAI,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeAI, siteInfo)
}

func (s *SiteInfoService) maskAIKeys(resp *schema.SiteAIResp) {
	for _, provider := range resp.SiteAIProviders {
		if provider.APIKey == "" {
			continue
		}
		provider.APIKey = strings.Repeat("*", len(provider.APIKey))
	}
}

func (s *SiteInfoService) restoreMaskedAIKeys(ctx context.Context, req *schema.SiteAIReq) error {
	hasMasked := false
	for _, provider := range req.SiteAIProviders {
		if provider.APIKey != "" && isAllMask(provider.APIKey) {
			hasMasked = true
			break
		}
	}
	if !hasMasked {
		return nil
	}

	current, err := s.siteInfoCommonService.GetSiteAI(ctx)
	if err != nil {
		return err
	}
	currentMapping := make(map[string]*schema.SiteAIProvider)
	for _, provider := range current.SiteAIProviders {
		currentMapping[provider.Provider] = provider
	}
	for _, provider := range req.SiteAIProviders {
		if provider.APIKey == "" || !isAllMask(provider.APIKey) {
			continue
		}
		if stored, ok := currentMapping[provider.Provider]; ok {
			provider.APIKey = stored.APIKey
		}
	}
	return nil
}

func isAllMask(value string) bool {
	return strings.Trim(value, "*") == ""
}

// GetSiteMCP get site MCP configuration
func (s *SiteInfoService) GetSiteMCP(ctx context.Context) (resp *schema.SiteMCPResp, err error) {
	resp, err = s.siteInfoCommonService.GetSiteMCP(ctx)
	if err != nil {
		return nil, err
	}
	siteInfo, err := s.GetSiteGeneral(ctx)
	if err != nil {
		return nil, err
	}

	resp.Type = "Server-Sent Event (SSE)"
	resp.URL = fmt.Sprintf("%s/answer/api/v1/mcp/sse", siteInfo.SiteUrl)
	resp.HTTPHeader = "Authorization={key}"
	return
}

// SaveSiteMCP save site MCP configuration
func (s *SiteInfoService) SaveSiteMCP(ctx context.Context, req *schema.SiteMCPReq) (err error) {
	content, _ := json.Marshal(req)
	siteInfo := &entity.SiteInfo{
		Type:    constant.SiteTypeMCP,
		Content: string(content),
		Status:  1,
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeMCP, siteInfo)
}

// GetSMTPConfig get smtp config
func (s *SiteInfoService) GetSMTPConfig(ctx context.Context) (resp *schema.GetSMTPConfigResp, err error) {
	emailConfig, err := s.emailService.GetEmailConfig(ctx)
	if err != nil {
		return nil, err
	}
	resp = &schema.GetSMTPConfigResp{}
	_ = copier.Copy(resp, emailConfig)
	resp.SMTPPassword = strings.Repeat("*", len(resp.SMTPPassword))
	return resp, nil
}

// UpdateSMTPConfig get smtp config
func (s *SiteInfoService) UpdateSMTPConfig(ctx context.Context, req *schema.UpdateSMTPConfigReq) (err error) {
	emailConfig, err := s.emailService.GetEmailConfig(ctx)
	if err != nil {
		return err
	}

	ec := &export.EmailConfig{}
	_ = copier.Copy(ec, req)

	if len(ec.SMTPPassword) > 0 && ec.SMTPPassword == strings.Repeat("*", len(ec.SMTPPassword)) {
		ec.SMTPPassword = emailConfig.SMTPPassword
	}

	err = s.emailService.SetEmailConfig(ctx, ec)
	if err != nil {
		return err
	}
	if len(req.TestEmailRecipient) > 0 {
		title, body, err := s.emailService.TestTemplate(ctx)
		if err != nil {
			return err
		}
		go s.emailService.Send(ctx, req.TestEmailRecipient, title, body)
	}
	return nil
}

func (s *SiteInfoService) GetSeo(ctx context.Context) (resp *schema.SiteSeoReq, err error) {
	resp = &schema.SiteSeoReq{}
	if err = s.siteInfoCommonService.GetSiteInfoByType(ctx, constant.SiteTypeSeo, resp); err != nil {
		return resp, err
	}
	siteSecurity, err := s.GetSiteSecurity(ctx)
	if err != nil {
		log.Error(err)
		return resp, nil
	}
	// If the site is set to privacy mode, prohibit crawling any page.
	if siteSecurity.LoginRequired {
		resp.Robots = "User-agent: *\nDisallow: /"
		return resp, nil
	}
	return resp, nil
}

func (s *SiteInfoService) SaveSeo(ctx context.Context, req schema.SiteSeoReq) (err error) {
	content, _ := json.Marshal(req)
	data := entity.SiteInfo{
		Type:    constant.SiteTypeSeo,
		Content: string(content),
	}
	return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeSeo, &data)
}

func (s *SiteInfoService) GetPrivilegesConfig(ctx context.Context) (resp *schema.GetPrivilegesConfigResp, err error) {
	privilege := &schema.UpdatePrivilegesConfigReq{}
	if err = s.siteInfoCommonService.GetSiteInfoByType(ctx, constant.SiteTypePrivileges, privilege); err != nil {
		return nil, err
	}
	privilegeOptions := schema.DefaultPrivilegeOptions
	if len(privilege.CustomPrivileges) > 0 {
		privilegeOptions = append(privilegeOptions, &schema.PrivilegeOption{
			Level:      schema.PrivilegeLevelCustom,
			LevelDesc:  reason.PrivilegeLevelCustomDesc,
			Privileges: privilege.CustomPrivileges,
		})
	} else {
		privilegeOptions = append(privilegeOptions, schema.DefaultCustomPrivilegeOption)
	}
	resp = &schema.GetPrivilegesConfigResp{
		Options:       s.translatePrivilegeOptions(ctx, privilegeOptions),
		SelectedLevel: schema.PrivilegeLevel3,
	}
	if privilege.Level > 0 {
		resp.SelectedLevel = privilege.Level
	}
	return resp, nil
}

func (s *SiteInfoService) translatePrivilegeOptions(ctx context.Context, privilegeOptions []*schema.PrivilegeOption) (options []*schema.PrivilegeOption) {
	la := handler.GetLangByCtx(ctx)
	for _, option := range privilegeOptions {
		op := &schema.PrivilegeOption{
			Level:     option.Level,
			LevelDesc: translator.Tr(la, option.LevelDesc),
		}
		for _, privilege := range option.Privileges {
			op.Privileges = append(op.Privileges, &constant.Privilege{
				Key:   privilege.Key,
				Label: translator.Tr(la, privilege.Label),
				Value: privilege.Value,
			})
		}
		options = append(options, op)
	}
	return
}

func (s *SiteInfoService) UpdatePrivilegesConfig(ctx context.Context, req *schema.UpdatePrivilegesConfigReq) (err error) {
	var choosePrivileges []*constant.Privilege
	if req.Level == schema.PrivilegeLevelCustom {
		choosePrivileges = req.CustomPrivileges
	} else {
		chooseOption := schema.DefaultPrivilegeOptions.Choose(req.Level)
		if chooseOption == nil {
			return nil
		}
		choosePrivileges = chooseOption.Privileges
	}
	if choosePrivileges == nil {
		return nil
	}

	// update site info that user choose which privilege level
	if req.Level == schema.PrivilegeLevelCustom {
		privilegeMap := make(map[string]int)
		for _, privilege := range req.CustomPrivileges {
			privilegeMap[privilege.Key] = privilege.Value
		}
		var privileges []*constant.Privilege
		for _, privilege := range constant.RankAllPrivileges {
			privileges = append(privileges, &constant.Privilege{
				Key:   privilege.Key,
				Label: privilege.Label,
				Value: privilegeMap[privilege.Key],
			})
		}
		req.CustomPrivileges = privileges
	} else {
		privilege := &schema.UpdatePrivilegesConfigReq{}
		if err = s.siteInfoCommonService.GetSiteInfoByType(ctx, constant.SiteTypePrivileges, privilege); err != nil {
			return err
		}
		req.CustomPrivileges = privilege.CustomPrivileges
	}

	content, _ := json.Marshal(req)
	data := &entity.SiteInfo{
		Type:    constant.SiteTypePrivileges,
		Content: string(content),
		Status:  1,
	}
	err = s.siteInfoRepo.SaveByType(ctx, constant.SiteTypePrivileges, data)
	if err != nil {
		return err
	}

	// update privilege in config
	for _, privilege := range choosePrivileges {
		err = s.configService.UpdateConfig(ctx, privilege.Key, fmt.Sprintf("%d", privilege.Value))
		if err != nil {
			return err
		}
	}
	return
}

func (s *SiteInfoService) CleanUpRemovedBrandingFiles(
	ctx context.Context,
	newBranding *schema.SiteBrandingReq,
	currentBranding *schema.SiteBrandingResp,
) error {
	var allErrors []error
	currentFiles := map[string]string{
		"logo":        currentBranding.Logo,
		"mobile_logo": currentBranding.MobileLogo,
		"square_icon": currentBranding.SquareIcon,
		"favicon":     currentBranding.Favicon,
	}

	newFiles := map[string]string{
		"logo":        newBranding.Logo,
		"mobile_logo": newBranding.MobileLogo,
		"square_icon": newBranding.SquareIcon,
		"favicon":     newBranding.Favicon,
	}

	for key, currentFile := range currentFiles {
		newFile := newFiles[key]
		if currentFile != "" && currentFile != newFile {
			fileRecord, err := s.fileRecordService.GetFileRecordByURL(ctx, currentFile)
			if err != nil {
				allErrors = append(allErrors, err)
				continue
			}
			if fileRecord == nil {
				err := errpkg.New("file record is nil for key " + key)
				allErrors = append(allErrors, err)
				continue
			}
			if err := s.fileRecordService.DeleteAndMoveFileRecord(ctx, fileRecord); err != nil {
				allErrors = append(allErrors, err)
			}
		}
	}
	if len(allErrors) > 0 {
		return errpkg.Join(allErrors...)
	}
	return nil
}

func (s *SiteInfoService) GetAIProvider(ctx context.Context) (resp []*schema.GetAIProviderResp, err error) {
	resp = make([]*schema.GetAIProviderResp, 0)
	aiProviderConfig, err := s.configService.GetStringValue(context.TODO(), constant.AIConfigProvider)
	if err != nil {
		log.Error(err)
		return resp, nil
	}

	_ = json.Unmarshal([]byte(aiProviderConfig), &resp)
	return resp, nil
}

func (s *SiteInfoService) GetAIModels(ctx context.Context, req *schema.GetAIModelsReq) (resp []*schema.GetAIModelResp, err error) {
	resp = make([]*schema.GetAIModelResp, 0)
	if req.APIKey != "" && isAllMask(req.APIKey) {
		storedKey, err := s.getStoredAIKey(ctx, req.APIHost)
		if err != nil {
			return resp, err
		}
		if storedKey == "" {
			return resp, errors.BadRequest("api_key is required")
		}
		req.APIKey = storedKey
	}

	r := resty.New()
	r.SetHeader("Authorization", fmt.Sprintf("Bearer %s", req.APIKey))
	r.SetHeader("Content-Type", "application/json")
	respBody, err := r.R().Get(req.APIHost + "/v1/models")
	if err != nil {
		log.Error(err)
		return resp, errors.BadRequest(fmt.Sprintf("failed to get AI models %s", err.Error()))
	}
	if !respBody.IsSuccess() {
		log.Error(fmt.Sprintf("failed to get AI models, status code: %d, body: %s", respBody.StatusCode(), respBody.String()))
		return resp, errors.BadRequest(fmt.Sprintf("failed to get AI models, response: %s", respBody.String()))
	}

	data := schema.GetAIModelsResp{}
	_ = json.Unmarshal(respBody.Body(), &data)

	for _, model := range data.Data {
		resp = append(resp, &schema.GetAIModelResp{
			Id:      model.Id,
			Object:  model.Object,
			Created: model.Created,
			OwnedBy: model.OwnedBy,
		})
	}
	return resp, nil
}

func (s *SiteInfoService) getStoredAIKey(ctx context.Context, apiHost string) (string, error) {
	current, err := s.siteInfoCommonService.GetSiteAI(ctx)
	if err != nil {
		return "", err
	}
	apiHost = strings.TrimRight(apiHost, "/")
	for _, provider := range current.SiteAIProviders {
		if strings.TrimRight(provider.APIHost, "/") == apiHost && provider.APIKey != "" {
			return provider.APIKey, nil
		}
	}
	if current.ChosenProvider != "" {
		for _, provider := range current.SiteAIProviders {
			if provider.Provider == current.ChosenProvider {
				return provider.APIKey, nil
			}
		}
	}
	return "", nil
}
