//
// Copyright (C) 2024 veypi <i@veypi.com>
// 2025-02-14 16:08:06
// Distributed under terms of the MIT license.
//

package auth

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/veypi/vbase/cfg"
	"github.com/veypi/vbase/models"
	"github.com/veypi/vigo"
)

// Auth 权限管理接口
type Auth interface {
	// ========== 中间件生成 ==========
	// 基础权限检查
	Perm(permissionID string) func(*vigo.X) error

	// 资源所有者权限
	PermWithOwner(permissionID, ownerKey string) func(*vigo.X) error

	// 满足任一权限
	PermAny(permissionIDs []string) func(*vigo.X) error

	// 满足所有权限
	PermAll(permissionIDs []string) func(*vigo.X) error

	// ========== 权限管理 ==========
	// 授予角色
	GrantRole(ctx context.Context, req models.GrantRoleRequest) error

	// 撤销角色
	RevokeRole(ctx context.Context, userID, orgID, roleCode string) error

	// 授予特定资源权限
	GrantResourcePerm(ctx context.Context, req models.GrantResourcePermRequest) error

	// 撤销特定资源权限
	RevokeResourcePerm(ctx context.Context, userID, orgID, permissionID, resourceID string) error

	// 撤销用户所有权限
	RevokeAll(ctx context.Context, userID, orgID string) error

	// ========== 权限查询 ==========
	// 检查权限
	CheckPermission(ctx context.Context, req models.CheckPermRequest) (bool, error)

	// 列出用户权限
	ListUserPermissions(ctx context.Context, userID, orgID string) ([]models.UserPermissionResult, error)

	// 列出资源授权用户
	ListResourceUsers(ctx context.Context, orgID, permissionID, resourceID string) ([]models.ResourceUser, error)
}

// 全局 Auth 工厂
var Factory = &authFactory{
	apps: make(map[string]*appAuth),
}

// VBaseAuth vbase 自身的权限管理实例
// 由 vbase 包在初始化时注入
var (
	VBaseAuth = Factory.New("vb", models.AppConfig{
		Name:        "VBase",
		Description: "VBase 基础设施",
		DefaultRoles: []models.RoleDefinition{
			{Code: "admin", Name: "管理员", Policies: []string{"*:*"}},
			{Code: "user", Name: "普通用户", Policies: []string{
				"user:read", "user:update",
				"org:read", "org:create",
				"oauth:client:read", "oauth:client:create", "oauth:client:update", "oauth:client:delete",
			}},
		},
	})
)

type authFactory struct {
	apps map[string]*appAuth // appKey -> auth实例
}

// New 创建权限管理实例（注册应用）
func (f *authFactory) New(appKey string, config models.AppConfig) Auth {
	if _, exists := f.apps[appKey]; exists {
		return f.apps[appKey]
	}

	auth := &appAuth{
		appKey: appKey,
		config: config,
	}
	f.apps[appKey] = auth
	return auth
}

// Init 初始化所有注册的权限配置
// - 检查不同 app 之间是否有冲突
// - 同步 Permission 到数据库
// - 建立预设角色
func (f *authFactory) Init() error {
	for appKey, auth := range f.apps {
		if err := auth.init(); err != nil {
			return fmt.Errorf("failed to init auth for %s: %w", appKey, err)
		}
	}

	return nil
}

// appAuth 单个应用的权限管理
type appAuth struct {
	appKey string
	config models.AppConfig
}

// init 初始化应用的权限配置
func (a *appAuth) init() error {
	// 1. 同步权限定义到数据库
	for _, permDef := range a.extractPermissions() {
		var perm models.Permission
		err := cfg.DB().Where("id = ?", permDef.ID).First(&perm).Error
		if err != nil {
			// 不存在则创建
			perm = permDef
			if err := cfg.DB().Create(&perm).Error; err != nil {
				return fmt.Errorf("failed to create permission %s: %w", permDef.ID, err)
			}
		}
	}

	// 2. 创建系统预设角色
	for _, roleDef := range a.config.DefaultRoles {
		if err := a.initRole(roleDef); err != nil {
			return err
		}
	}

	return nil
}

// extractPermissions 从角色定义中提取所有权限
func (a *appAuth) extractPermissions() []models.Permission {
	permMap := make(map[string]models.Permission)

	for _, roleDef := range a.config.DefaultRoles {
		for _, policy := range roleDef.Policies {
			// policy 格式: "resource:action" 或 "*:*"
			parts := strings.Split(policy, ":")
			if len(parts) != 2 {
				continue
			}

			resource, action := parts[0], parts[1]
			permID := fmt.Sprintf("%s:%s:%s", a.appKey, resource, action)

			if _, exists := permMap[permID]; !exists {
				permMap[permID] = models.Permission{
					ID:          permID,
					AppKey:      a.appKey,
					Resource:    resource,
					Action:      action,
					Description: fmt.Sprintf("%s %s on %s", a.config.Name, action, resource),
				}
			}
		}
	}

	result := make([]models.Permission, 0, len(permMap))
	for _, perm := range permMap {
		result = append(result, perm)
	}
	return result
}

// initRole 初始化系统预设角色
func (a *appAuth) initRole(roleDef models.RoleDefinition) error {
	// 查找或创建系统角色
	var role models.Role
	err := cfg.DB().Where("code = ? AND org_id = ''", roleDef.Code).First(&role).Error
	if err != nil {
		// 创建新角色
		role = models.Role{
			OrgID:       "",
			Code:        roleDef.Code,
			Name:        roleDef.Name,
			Description: roleDef.Description,
			IsSystem:    true,
			Status:      1,
		}
		if err := cfg.DB().Create(&role).Error; err != nil {
			return fmt.Errorf("failed to create role %s: %w", roleDef.Code, err)
		}
	}

	// 同步角色权限
	for _, policy := range roleDef.Policies {
		parts := strings.Split(policy, ":")
		if len(parts) != 2 {
			continue
		}

		resource, action := parts[0], parts[1]
		permID := fmt.Sprintf("%s:%s:%s", a.appKey, resource, action)

		// 检查关联是否存在
		var count int64
		cfg.DB().Model(&models.RolePermission{}).
			Where("role_id = ? AND permission_id = ?", role.ID, permID).
			Count(&count)

		if count == 0 {
			rp := models.RolePermission{
				RoleID:       role.ID,
				PermissionID: permID,
				Condition:    "none",
			}
			if err := cfg.DB().Create(&rp).Error; err != nil {
				return fmt.Errorf("failed to create role permission: %w", err)
			}
		}
	}

	return nil
}

// ========== 中间件实现 ==========

func (a *appAuth) Perm(permissionID string) func(*vigo.X) error {
	return func(x *vigo.X) error {
		userID := getUserID(x)
		if userID == "" {
			return vigo.ErrUnauthorized
		}

		orgID := getOrgID(x)

		ok, err := a.CheckPermission(x.Context(), models.CheckPermRequest{
			UserID:       userID,
			OrgID:        orgID,
			PermissionID: permissionID,
		})
		if err != nil {
			return err
		}
		if !ok {
			return vigo.ErrForbidden
		}
		return nil
	}
}

func (a *appAuth) PermWithOwner(permissionID, ownerKey string) func(*vigo.X) error {
	return func(x *vigo.X) error {
		userID := getUserID(x)
		if userID == "" {
			return vigo.ErrUnauthorized
		}

		orgID := getOrgID(x)

		// 先检查是否有权限
		ok, err := a.CheckPermission(x.Context(), models.CheckPermRequest{
			UserID:       userID,
			OrgID:        orgID,
			PermissionID: permissionID,
		})
		if err != nil {
			return err
		}
		if !ok {
			return vigo.ErrForbidden
		}

		// 检查是否是所有者或管理员
		ownerID, _ := x.Get(ownerKey).(string)
		if ownerID == "" {
			ownerID = x.PathParams.Get(ownerKey)
		}
		if ownerID == userID {
			return nil
		}

		// 检查是否是管理员
		isAdmin, _ := a.isAdmin(x.Context(), userID, orgID)
		if isAdmin {
			return nil
		}

		return vigo.ErrForbidden
	}
}

func (a *appAuth) PermAny(permissionIDs []string) func(*vigo.X) error {
	return func(x *vigo.X) error {
		userID := getUserID(x)
		if userID == "" {
			return vigo.ErrUnauthorized
		}

		orgID := getOrgID(x)

		for _, permID := range permissionIDs {
			ok, err := a.CheckPermission(x.Context(), models.CheckPermRequest{
				UserID:       userID,
				OrgID:        orgID,
				PermissionID: permID,
			})
			if err != nil {
				return err
			}
			if ok {
				return nil
			}
		}

		return vigo.ErrForbidden
	}
}

func (a *appAuth) PermAll(permissionIDs []string) func(*vigo.X) error {
	return func(x *vigo.X) error {
		userID := getUserID(x)
		if userID == "" {
			return vigo.ErrUnauthorized
		}

		orgID := getOrgID(x)

		for _, permID := range permissionIDs {
			ok, err := a.CheckPermission(x.Context(), models.CheckPermRequest{
				UserID:       userID,
				OrgID:        orgID,
				PermissionID: permID,
			})
			if err != nil {
				return err
			}
			if !ok {
				return vigo.ErrForbidden
			}
		}

		return nil
	}
}

// ========== 权限管理实现 ==========

func (a *appAuth) GrantRole(ctx context.Context, req models.GrantRoleRequest) error {
	// 查找角色
	var role models.Role
	query := cfg.DB().Where("code = ?", req.RoleCode)
	if req.OrgID != "" {
		query = query.Where("org_id = ?", req.OrgID)
	} else {
		query = query.Where("org_id = ''")
	}

	if err := query.First(&role).Error; err != nil {
		// 如果指定了 OrgID 但没找到，尝试查找全局角色
		if req.OrgID != "" {
			query = cfg.DB().Where("code = ? AND org_id = ''", req.RoleCode)
			if err := query.First(&role).Error; err != nil {
				return fmt.Errorf("role not found: %s", req.RoleCode)
			}
		} else {
			return fmt.Errorf("role not found: %s", req.RoleCode)
		}
	}

	// 检查是否已存在
	var count int64
	cfg.DB().Model(&models.UserRole{}).
		Where("user_id = ? AND org_id = ? AND role_id = ?", req.UserID, req.OrgID, role.ID).
		Count(&count)

	if count > 0 {
		return nil // 已存在
	}

	userRole := models.UserRole{
		UserID:   req.UserID,
		OrgID:    req.OrgID,
		RoleID:   role.ID,
		ExpireAt: req.ExpireAt,
	}

	return cfg.DB().Create(&userRole).Error
}

func (a *appAuth) RevokeRole(ctx context.Context, userID, orgID, roleCode string) error {
	var role models.Role
	// 优先查找组织特定角色
	query := cfg.DB().Where("code = ?", roleCode)
	if orgID != "" {
		query = query.Where("org_id = ?", orgID)
	} else {
		query = query.Where("org_id = ''")
	}

	if err := query.First(&role).Error; err != nil {
		// 如果没找到，尝试查找全局角色
		if orgID != "" {
			if err := cfg.DB().Where("code = ? AND org_id = ''", roleCode).First(&role).Error; err != nil {
				return nil // 角色不存在，无需撤销
			}
		} else {
			return nil
		}
	}

	return cfg.DB().Where("user_id = ? AND org_id = ? AND role_id = ?", userID, orgID, role.ID).
		Delete(&models.UserRole{}).Error
}

func (a *appAuth) GrantResourcePerm(ctx context.Context, req models.GrantResourcePermRequest) error {
	// 检查权限是否存在
	var perm models.Permission
	if err := cfg.DB().Where("id = ?", req.PermissionID).First(&perm).Error; err != nil {
		return fmt.Errorf("permission not found: %s", req.PermissionID)
	}

	// 检查是否已存在
	var existing models.UserPermission
	err := cfg.DB().Where("user_id = ? AND org_id = ? AND permission_id = ? AND resource_id = ?",
		req.UserID, req.OrgID, req.PermissionID, req.ResourceID).
		First(&existing).Error

	if err == nil {
		// 更新过期时间
		existing.ExpireAt = req.ExpireAt
		return cfg.DB().Save(&existing).Error
	}

	userPerm := models.UserPermission{
		UserID:       req.UserID,
		OrgID:        req.OrgID,
		PermissionID: req.PermissionID,
		ResourceID:   req.ResourceID,
		ExpireAt:     req.ExpireAt,
		GrantedBy:    req.GrantedBy,
	}

	return cfg.DB().Create(&userPerm).Error
}

func (a *appAuth) RevokeResourcePerm(ctx context.Context, userID, orgID, permissionID, resourceID string) error {
	return cfg.DB().Where("user_id = ? AND org_id = ? AND permission_id = ? AND resource_id = ?",
		userID, orgID, permissionID, resourceID).
		Delete(&models.UserPermission{}).Error
}

func (a *appAuth) RevokeAll(ctx context.Context, userID, orgID string) error {
	// 删除用户角色
	if err := cfg.DB().Where("user_id = ? AND org_id = ?", userID, orgID).
		Delete(&models.UserRole{}).Error; err != nil {
		return err
	}

	// 删除用户特定权限
	if err := cfg.DB().Where("user_id = ? AND org_id = ?", userID, orgID).
		Delete(&models.UserPermission{}).Error; err != nil {
		return err
	}

	return nil
}

// ========== 权限查询实现 ==========

func (a *appAuth) CheckPermission(ctx context.Context, req models.CheckPermRequest) (bool, error) {
	// 1. 检查用户是否有该权限的角色
	var roleIDs []string
	if err := cfg.DB().Model(&models.UserRole{}).
		Where("user_id = ? AND org_id = ? AND (expire_at IS NULL OR expire_at > ?)",
			req.UserID, req.OrgID, time.Now()).
		Pluck("role_id", &roleIDs).Error; err != nil {
		return false, err
	}

	if len(roleIDs) > 0 {
		// 检查这些角色是否有所需权限
		var count int64
		if err := cfg.DB().Model(&models.RolePermission{}).
			Where("role_id IN ? AND permission_id = ?", roleIDs, req.PermissionID).
			Count(&count).Error; err != nil {
			return false, err
		}
		if count > 0 {
			return true, nil
		}
	}

	// 2. 检查用户是否有特定的资源权限
	var userPermCount int64
	query := cfg.DB().Model(&models.UserPermission{}).
		Where("user_id = ? AND org_id = ? AND permission_id = ? AND (expire_at IS NULL OR expire_at > ?)",
			req.UserID, req.OrgID, req.PermissionID, time.Now())

	if req.ResourceID != "" {
		query = query.Where("resource_id = ? OR resource_id = '*'", req.ResourceID)
	}

	if err := query.Count(&userPermCount).Error; err != nil {
		return false, err
	}

	return userPermCount > 0, nil
}

func (a *appAuth) ListUserPermissions(ctx context.Context, userID, orgID string) ([]models.UserPermissionResult, error) {
	result := make([]models.UserPermissionResult, 0)

	// 1. 获取用户角色对应的权限
	var roleIDs []string
	if err := cfg.DB().Model(&models.UserRole{}).
		Where("user_id = ? AND org_id = ? AND (expire_at IS NULL OR expire_at > ?)",
			userID, orgID, time.Now()).
		Pluck("role_id", &roleIDs).Error; err != nil {
		return nil, err
	}

	if len(roleIDs) > 0 {
		var permIDs []string
		if err := cfg.DB().Model(&models.RolePermission{}).
			Where("role_id IN ?", roleIDs).
			Pluck("permission_id", &permIDs).Error; err != nil {
			return nil, err
		}

		for _, permID := range permIDs {
			result = append(result, models.UserPermissionResult{
				PermissionID: permID,
				ResourceID:   "*",
				Actions:      []string{"*"},
			})
		}
	}

	// 2. 获取用户特定资源权限
	var userPerms []models.UserPermission
	if err := cfg.DB().Where("user_id = ? AND org_id = ? AND (expire_at IS NULL OR expire_at > ?)",
		userID, orgID, time.Now()).
		Find(&userPerms).Error; err != nil {
		return nil, err
	}

	for _, up := range userPerms {
		result = append(result, models.UserPermissionResult{
			PermissionID: up.PermissionID,
			ResourceID:   up.ResourceID,
			Actions:      []string{"*"},
		})
	}

	return result, nil
}

func (a *appAuth) ListResourceUsers(ctx context.Context, orgID, permissionID, resourceID string) ([]models.ResourceUser, error) {
	result := make([]models.ResourceUser, 0)

	// 查询有该资源权限的用户
	var userPerms []models.UserPermission
	query := cfg.DB().Where("org_id = ? AND permission_id = ?", orgID, permissionID)
	if resourceID != "" {
		query = query.Where("resource_id = ? OR resource_id = '*'", resourceID)
	}

	if err := query.Find(&userPerms).Error; err != nil {
		return nil, err
	}

	userMap := make(map[string][]string)
	for _, up := range userPerms {
		userMap[up.UserID] = append(userMap[up.UserID], "*")
	}

	for userID, actions := range userMap {
		result = append(result, models.ResourceUser{
			UserID:  userID,
			Actions: actions,
		})
	}

	return result, nil
}

// ========== 辅助方法 ==========

func (a *appAuth) isAdmin(ctx context.Context, userID, orgID string) (bool, error) {
	// 检查用户是否有管理员角色
	var adminRoleIDs []string
	if err := cfg.DB().Model(&models.Role{}).
		Where("code = 'admin'").
		Pluck("id", &adminRoleIDs).Error; err != nil {
		return false, err
	}

	if len(adminRoleIDs) == 0 {
		return false, nil
	}

	var count int64
	if err := cfg.DB().Model(&models.UserRole{}).
		Where("user_id = ? AND org_id = ? AND role_id IN ?", userID, orgID, adminRoleIDs).
		Count(&count).Error; err != nil {
		return false, err
	}

	return count > 0, nil
}

// 从 context 获取用户ID
func getUserID(x *vigo.X) string {
	if userID, ok := x.Get("user_id").(string); ok {
		return userID
	}
	return ""
}

// 从 context 获取组织ID
func getOrgID(x *vigo.X) string {
	if orgID, ok := x.Get("org_id").(string); ok {
		return orgID
	}
	return ""
}
