package service

import (
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"golang.org/x/oauth2"

	"be.ems/src/framework/config"
	"be.ems/src/framework/constants"
	"be.ems/src/framework/token"
	"be.ems/src/framework/utils/parse"
	"be.ems/src/modules/auth/model"
	systemModelVo "be.ems/src/modules/system/model/vo"
)

// ByOAuth2CodeURL 获取OAuth2登录URL
func (s *Account) ByOAuth2CodeURL(state string) (string, error) {
	rows := s.sysLogSourceService.FindByActive(state)
	if len(rows) != 1 {
		return "", fmt.Errorf("oauth2 auth source not exist")
	}
	item := rows[0]
	if item.Config == "" {
		return "", fmt.Errorf("oauth2 auth source config is empty")
	}
	var source systemModelVo.SysLoginSourceOAuth2
	json.Unmarshal([]byte(item.Config), &source)

	conf := oauth2.Config{
		ClientID:     source.ClientID,
		ClientSecret: source.ClientSecret,
		RedirectURL:  source.RedirectURL,
		Scopes:       source.Scopes,
		Endpoint: oauth2.Endpoint{
			AuthURL:  source.AuthURL,
			TokenURL: source.TokenURL,
		},
	}
	return conf.AuthCodeURL(state, oauth2.AccessTypeOffline), nil
}

// ByOAuth2 登录创建用户信息
func (s *Account) ByOAuth2(body model.LoginSourceOauth2Body) (token.UserInfo, error) {
	info := token.UserInfo{}
	rows := s.sysLogSourceService.FindByActive(body.State)
	if len(rows) != 1 {
		return info, fmt.Errorf("oauth2 auth source not exist")
	}
	item := rows[0]
	if item.Config == "" {
		return info, fmt.Errorf("oauth2 auth source config is empty")
	}
	var source systemModelVo.SysLoginSourceOAuth2
	if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
		return info, err
	}

	// 校验OAuth2用户
	account, err := oauth2Auth(source, body.Code)
	if err != nil {
		return info, err
	}

	// 查询用户登录账号
	sysUser := s.sysUserService.FindByUserName(account, item.Type, item.UID)
	if sysUser.UserId == 0 || sysUser.UserName == "" {
		sysUser = s.initLoginSourceUser(item.UID, item.Type, account, account)
	}
	if sysUser.UserId == 0 || sysUser.UserName != account {
		return info, fmt.Errorf("login.errNameOrPasswd")
	}
	if sysUser.DelFlag == constants.STATUS_YES {
		return info, fmt.Errorf("login.errDelFlag")
	}
	if sysUser.StatusFlag == constants.STATUS_NO {
		return info, fmt.Errorf("login.errStatus")
	}

	// 登录用户信息
	info.UserId = sysUser.UserId
	info.DeptId = sysUser.DeptId
	info.User = sysUser
	// 用户权限组标识
	if config.IsSystemUser(sysUser.UserId) {
		info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
	} else {
		perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
		info.Permissions = parse.RemoveDuplicates(perms)
	}
	return info, nil
}

// oauth2Auth 校验OAuth2用户
func oauth2Auth(source systemModelVo.SysLoginSourceOAuth2, code string) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	conf := oauth2.Config{
		ClientID:     source.ClientID,
		ClientSecret: source.ClientSecret,
		RedirectURL:  source.RedirectURL,
		Scopes:       source.Scopes,
		Endpoint: oauth2.Endpoint{
			AuthURL:  source.AuthURL,
			TokenURL: source.TokenURL,
		},
	}
	token, err := conf.Exchange(ctx, code)
	if err != nil {
		return "", err
	}
	// 使用token创建HTTP客户端 请求用户信息
	resp, err := conf.Client(ctx, token).Get(source.UserURL)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	// 解析用户信息
	var userInfo map[string]any
	if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
		return "", err
	}

	// 读取嵌套数据
	value, found := getValueByPath(userInfo, source.AccountField)
	if !found {
		return "", fmt.Errorf("oauth2 auth source account field not exist")
	}
	return fmt.Sprintf("%v", value), nil
}

// getValueByPath 从嵌套的 map[string]any 获取嵌套键对应的值
func getValueByPath(data map[string]any, path string) (any, bool) {
	keys := strings.Split(path, ".") // 按照 "." 拆分路径
	return getValue(data, keys)
}

// getValue 是递归查找嵌套 map 的函数
func getValue(data map[string]any, keys []string) (any, bool) {
	if len(keys) == 0 {
		return data, false
	}

	// 获取当前键
	key := keys[0]

	// 获取当前键的值
	val, ok := data[key]
	if !ok {
		return nil, false // 找不到键，返回 false
	}

	// 如果还有嵌套键，继续查找
	if len(keys) > 1 {
		// 递归查找嵌套 map
		if reflect.TypeOf(val).Kind() == reflect.Map {
			// 将 `any` 转换为 `map[string]any` 类型
			if nestedMap, ok := val.(map[string]any); ok {
				return getValue(nestedMap, keys[1:])
			}
		}
	}
	return val, true
}
