package oci

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"math/big"
	"slices"
	"strings"
	"time"

	"github.com/oracle/oci-go-sdk/v65/common"
	"github.com/oracle/oci-go-sdk/v65/identity"
	"github.com/oracle/oci-go-sdk/v65/identitydomains"
)

// TenantUser 是租户下的一个 IAM 用户。
type TenantUser struct {
	ID             string     `json:"id"`
	Name           string     `json:"name"`
	Description    string     `json:"description"`
	Email          string     `json:"email"`
	EmailVerified  bool       `json:"emailVerified"`
	MfaActivated   bool       `json:"mfaActivated"`
	LifecycleState string     `json:"lifecycleState"`
	IsCurrentUser  bool       `json:"isCurrentUser"`
	TimeCreated    *time.Time `json:"timeCreated"`
	LastLoginTime  *time.Time `json:"lastLoginTime,omitempty"`
}

// Oracle 预置的「身份域管理员」应用角色显示名。
const domainAdminRoleName = "Identity Domain Administrator"

// adminGroupNames 是租户管理员组候选名(按优先序):原生域为 Administrators,
// IDCS 迁移域(idcs foundation,如 OracleIdentityCloudService)为 OCI_Administrators;
// 迁移域另有 IDCS_Administrators 属身份域管理范畴,由上面的应用角色概念覆盖,不算在内。
var adminGroupNames = []string{"Administrators", "OCI_Administrators"}

// isAdminGroupName 判断组显示名是否为租户管理员组。
func isAdminGroupName(name string) bool {
	return slices.Contains(adminGroupNames, name)
}

// scimAppRolesAttr 是 SCIM 用户 appRoles 扩展属性的完整路径。
const scimAppRolesAttr = "urn:ietf:params:scim:schemas:oracle:idcs:extension:user:User:appRoles"

// TenantUserDetail 是编辑表单需要的域档案与管理员状态；
// InDomain 为 false 表示经典 IAM 用户，其余字段无意义。
type TenantUserDetail struct {
	InDomain      bool   `json:"inDomain"`
	GivenName     string `json:"givenName"`
	FamilyName    string `json:"familyName"`
	IsDomainAdmin bool   `json:"isDomainAdmin"`
	InAdminGroup  bool   `json:"inAdminGroup"`
}

// GetTenantUserDetail 实现 Client：查用户域档案与管理员状态（编辑表单预填充用）。
func (c *RealClient) GetTenantUserDetail(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) (TenantUserDetail, error) {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return TenantUserDetail{}, err
	}
	u, found, err := domainUserByOcid(ctx, dc, userID, "groups", scimAppRolesAttr)
	if err != nil || !found {
		return TenantUserDetail{}, err
	}
	return toTenantUserDetail(u), nil
}

// toTenantUserDetail 提取姓名、管理员角色与管理员组成员状态。
func toTenantUserDetail(u identitydomains.User) TenantUserDetail {
	d := TenantUserDetail{InDomain: true}
	if u.Name != nil {
		d.GivenName = deref(u.Name.GivenName)
		d.FamilyName = deref(u.Name.FamilyName)
	}
	for _, g := range u.Groups {
		if isAdminGroupName(deref(g.Display)) {
			d.InAdminGroup = true
		}
	}
	if ext := u.UrnIetfParamsScimSchemasOracleIdcsExtensionUserUser; ext != nil {
		for _, r := range ext.AppRoles {
			if deref(r.Display) == domainAdminRoleName {
				d.IsDomainAdmin = true
			}
		}
	}
	return d
}

func (c *RealClient) identityClientAt(cred Credentials, region string) (identity.IdentityClient, error) {
	ic, err := identity.NewIdentityClientWithConfigurationProvider(provider(cred))
	if err != nil {
		return ic, fmt.Errorf("new identity client: %w", err)
	}
	applyProxy(&ic.BaseClient, cred)
	if region != "" {
		ic.SetRegion(normalizeRegion(region))
	}
	return ic, nil
}

// ListTenantUsers 实现 Client：列出租户 IAM 用户。
// domainID 非空时经 Identity Domains SCIM 只列该域用户;
// 为空时走经典 IAM(跨域拍平视图,兼容无域老租户)。
func (c *RealClient) ListTenantUsers(ctx context.Context, cred Credentials, homeRegion, domainID string) ([]TenantUser, error) {
	if domainID != "" {
		return c.listDomainUsers(ctx, cred, homeRegion, domainID)
	}
	ic, err := c.identityClientAt(cred, "")
	if err != nil {
		return nil, err
	}
	var users []TenantUser
	var page *string
	for {
		resp, err := ic.ListUsers(ctx, identity.ListUsersRequest{CompartmentId: &cred.TenancyOCID, Page: page})
		if err != nil {
			return nil, fmt.Errorf("list users: %w", err)
		}
		for _, item := range resp.Items {
			users = append(users, toTenantUser(item, cred.UserOCID))
		}
		if resp.OpcNextPage == nil {
			return orEmpty(users), nil
		}
		page = resp.OpcNextPage
	}
}

// scimListAttrs 是按域列用户时请求的属性集:列表字段 + MFA 与最近登录扩展。
const scimListAttrs = "id,ocid,userName,description,emails,active,meta.created," +
	"urn:ietf:params:scim:schemas:oracle:idcs:extension:mfa:User:mfaStatus," +
	"urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User:lastSuccessfulLoginDate"

// maxScimUserPages 限制按域列用户的翻页数(每页 200,上限 5000 人)。
const maxScimUserPages = 25

// listDomainUsers 经 SCIM 按 startIndex 翻页列出域内全部用户。
func (c *RealClient) listDomainUsers(ctx context.Context, cred Credentials, homeRegion, domainID string) ([]TenantUser, error) {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return nil, err
	}
	var users []TenantUser
	attrs := scimListAttrs
	count := 200
	start := 1
	for range maxScimUserPages {
		req := identitydomains.ListUsersRequest{Attributes: &attrs, Count: &count, StartIndex: &start}
		resp, err := dc.ListUsers(ctx, req)
		if err != nil {
			return nil, fmt.Errorf("list domain users: %w", err)
		}
		for _, u := range resp.Resources {
			users = append(users, toDomainListUser(u, cred.UserOCID))
		}
		start += len(resp.Resources)
		if resp.TotalResults == nil || start > *resp.TotalResults || len(resp.Resources) == 0 {
			return orEmpty(users), nil
		}
	}
	return orEmpty(users), nil
}

// toDomainListUser 把 SCIM 用户映射为列表 DTO;ID 用 IAM OCID
// 保持与经典路径同一 id 语义(IsCurrentUser 比对、后续操作路由)。
func toDomainListUser(u identitydomains.User, currentUserOCID string) TenantUser {
	out := toDomainTenantUser(u)
	out.IsCurrentUser = out.ID == currentUserOCID
	if u.Active != nil && !*u.Active {
		out.LifecycleState = "INACTIVE"
	}
	if u.Meta != nil {
		out.TimeCreated = parseScimTime(u.Meta.Created)
	}
	if ext := u.UrnIetfParamsScimSchemasOracleIdcsExtensionMfaUser; ext != nil {
		out.MfaActivated = ext.MfaStatus == identitydomains.ExtensionMfaUserMfaStatusEnrolled
	}
	if ext := u.UrnIetfParamsScimSchemasOracleIdcsExtensionUserStateUser; ext != nil {
		out.LastLoginTime = parseScimTime(ext.LastSuccessfulLoginDate)
	}
	for _, e := range u.Emails {
		if e.Primary != nil && *e.Primary {
			out.EmailVerified = e.Verified != nil && *e.Verified
		}
	}
	return out
}

// parseScimTime 解析 SCIM 的 RFC3339 时间串;空值或格式异常返回 nil。
func parseScimTime(s *string) *time.Time {
	if s == nil || *s == "" {
		return nil
	}
	t, err := time.Parse(time.RFC3339, *s)
	if err != nil {
		return nil
	}
	return &t
}

// CreateTenantUserInput 是创建用户的输入；GivenName/FamilyName 走
// Identity Domains SCIM（经典 IAM 无对应字段）。两个管理员选项同时勾选时
// 先授身份域管理员角色，再加入 Administrators 组。
type CreateTenantUserInput struct {
	Name             string // 登录用户名
	Description      string
	Email            string
	GivenName        string
	FamilyName       string
	GrantDomainAdmin bool // 授予「身份域管理员」应用角色
	AddToAdminGroup  bool // 加入 Administrators 组
}

// CreateTenantUser 实现 Client：创建用户（须发往 home region）。
// 优先 Identity Domains SCIM（支持姓名与管理员授权）；SCIM 不可用时回退
// 经典 IAM API，此时管理员选项无法执行、姓名并入描述。
// domainID 非空表示用户显式选择了目标域,SCIM 失败不再回退(避免建错域)。
func (c *RealClient) CreateTenantUser(ctx context.Context, cred Credentials, homeRegion, domainID string, in CreateTenantUserInput) (TenantUser, error) {
	user, err := c.createUserViaDomain(ctx, cred, homeRegion, domainID, in)
	if err == nil {
		return user, nil
	}
	if domainID != "" || in.GrantDomainAdmin || in.AddToAdminGroup {
		return TenantUser{}, fmt.Errorf("create user via identity domain: %w", err)
	}
	return c.createUserClassic(ctx, cred, homeRegion, in)
}

// createUserClassic 走经典 IAM API 创建用户（姓名并入描述）。
func (c *RealClient) createUserClassic(ctx context.Context, cred Credentials, homeRegion string, in CreateTenantUserInput) (TenantUser, error) {
	ic, err := c.identityClientAt(cred, homeRegion)
	if err != nil {
		return TenantUser{}, err
	}
	description := in.Description
	if description == "" {
		description = strings.TrimSpace(in.GivenName + " " + in.FamilyName)
	}
	details := identity.CreateUserDetails{
		CompartmentId: &cred.TenancyOCID,
		Name:          &in.Name,
		Description:   &description,
	}
	if in.Email != "" {
		details.Email = &in.Email
	}
	resp, err := ic.CreateUser(ctx, identity.CreateUserRequest{CreateUserDetails: details})
	if err != nil {
		return TenantUser{}, fmt.Errorf("create user %s: %w", in.Name, err)
	}
	return toTenantUser(resp.User, cred.UserOCID), nil
}

// createUserViaDomain 通过 Identity Domains SCIM 创建用户并按选项授权。
func (c *RealClient) createUserViaDomain(ctx context.Context, cred Credentials, homeRegion, domainID string, in CreateTenantUserInput) (TenantUser, error) {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return TenantUser{}, err
	}
	resp, err := dc.CreateUser(ctx, identitydomains.CreateUserRequest{User: buildScimUser(in)})
	if err != nil {
		return TenantUser{}, fmt.Errorf("create domain user %s: %w", in.Name, err)
	}
	scimID := deref(resp.User.Id)
	// 同时勾选时约定先授身份域管理员，再加管理员组
	if in.GrantDomainAdmin {
		if err := grantDomainAdminRole(ctx, dc, scimID); err != nil {
			return toDomainTenantUser(resp.User), err
		}
	}
	if in.AddToAdminGroup {
		if err := addUserToAdminGroup(ctx, dc, scimID); err != nil {
			return toDomainTenantUser(resp.User), err
		}
	}
	return toDomainTenantUser(resp.User), nil
}

func buildScimUser(in CreateTenantUserInput) identitydomains.User {
	u := identitydomains.User{
		Schemas:  []string{"urn:ietf:params:scim:schemas:core:2.0:User"},
		UserName: &in.Name,
		Name: &identitydomains.UserName{
			GivenName:  &in.GivenName,
			FamilyName: &in.FamilyName,
		},
	}
	if in.Description != "" {
		u.Description = &in.Description
	}
	if in.Email != "" {
		primary := true
		u.Emails = []identitydomains.UserEmails{{
			Value:   &in.Email,
			Type:    identitydomains.UserEmailsTypeWork,
			Primary: &primary,
		}}
	}
	return u
}

// findDomainAdminRole 查找「Identity Domain Administrator」应用角色。
func findDomainAdminRole(ctx context.Context, dc identitydomains.IdentityDomainsClient) (identitydomains.AppRole, error) {
	filter := fmt.Sprintf("displayName eq %q", domainAdminRoleName)
	count := 1
	roles, err := dc.ListAppRoles(ctx, identitydomains.ListAppRolesRequest{
		Filter:     &filter,
		Attributes: common.String("id,displayName,app"),
		Count:      &count,
	})
	if err != nil {
		return identitydomains.AppRole{}, fmt.Errorf("find domain administrator role: %w", err)
	}
	if len(roles.Resources) == 0 || roles.Resources[0].Id == nil || roles.Resources[0].App == nil {
		return identitydomains.AppRole{}, fmt.Errorf("identity domain administrator role not found")
	}
	return roles.Resources[0], nil
}

// grantDomainAdminRole 授予「Identity Domain Administrator」应用角色。
func grantDomainAdminRole(ctx context.Context, dc identitydomains.IdentityDomainsClient, userSCIMID string) error {
	role, err := findDomainAdminRole(ctx, dc)
	if err != nil {
		return err
	}
	_, err = dc.CreateGrant(ctx, identitydomains.CreateGrantRequest{
		Grant: identitydomains.Grant{
			Schemas:        []string{"urn:ietf:params:scim:schemas:oracle:idcs:Grant"},
			GrantMechanism: identitydomains.GrantGrantMechanismAdministratorToUser,
			Grantee: &identitydomains.GrantGrantee{
				Type:  identitydomains.GrantGranteeTypeUser,
				Value: &userSCIMID,
			},
			App: &identitydomains.GrantApp{Value: role.App.Value},
			Entitlement: &identitydomains.GrantEntitlement{
				AttributeName:  common.String("appRoles"),
				AttributeValue: role.Id,
			},
		},
	})
	if err != nil {
		return fmt.Errorf("grant domain administrator role: %w", err)
	}
	return nil
}

// revokeDomainAdminRole 撤销直接授予用户的「身份域管理员」角色；
// 只删 ADMINISTRATOR_TO_USER 机制的授权，经组继承的不动。
func revokeDomainAdminRole(ctx context.Context, dc identitydomains.IdentityDomainsClient, userSCIMID string) error {
	role, err := findDomainAdminRole(ctx, dc)
	if err != nil {
		return err
	}
	filter := fmt.Sprintf("grantee.value eq %q and entitlement.attributeValue eq %q", userSCIMID, deref(role.Id))
	grants, err := dc.ListGrants(ctx, identitydomains.ListGrantsRequest{
		Filter:     &filter,
		Attributes: common.String("id,grantMechanism"),
	})
	if err != nil {
		return fmt.Errorf("find domain administrator grants: %w", err)
	}
	for _, g := range grants.Resources {
		if g.GrantMechanism != identitydomains.GrantGrantMechanismAdministratorToUser {
			continue
		}
		if _, err := dc.DeleteGrant(ctx, identitydomains.DeleteGrantRequest{GrantId: g.Id}); err != nil {
			return fmt.Errorf("revoke domain administrator role: %w", err)
		}
	}
	return nil
}

// addUserToAdminGroup 把用户加入 Administrators 组。
func addUserToAdminGroup(ctx context.Context, dc identitydomains.IdentityDomainsClient, userSCIMID string) error {
	group, err := adminGroupRef(ctx, dc)
	if err != nil {
		return err
	}
	if group == nil {
		return fmt.Errorf("administrators group not found")
	}
	var members interface{} = []map[string]string{{"value": userSCIMID, "type": "User"}}
	_, err = dc.PatchGroup(ctx, identitydomains.PatchGroupRequest{
		GroupId: group.Value,
		PatchOp: identitydomains.PatchOp{
			Schemas: []string{"urn:ietf:params:scim:api:messages:2.0:PatchOp"},
			Operations: []identitydomains.Operations{{
				Op:    identitydomains.OperationsOpAdd,
				Path:  common.String("members"),
				Value: &members,
			}},
		},
	})
	if err != nil {
		return fmt.Errorf("add user to administrators group: %w", err)
	}
	return nil
}

// removeUserFromAdminGroup 把用户移出 Administrators 组。
func removeUserFromAdminGroup(ctx context.Context, dc identitydomains.IdentityDomainsClient, userSCIMID string) error {
	group, err := adminGroupRef(ctx, dc)
	if err != nil {
		return err
	}
	if group == nil {
		return fmt.Errorf("administrators group not found")
	}
	path := fmt.Sprintf("members[value eq %q]", userSCIMID)
	_, err = dc.PatchGroup(ctx, identitydomains.PatchGroupRequest{
		GroupId: group.Value,
		PatchOp: identitydomains.PatchOp{
			Schemas: []string{"urn:ietf:params:scim:api:messages:2.0:PatchOp"},
			Operations: []identitydomains.Operations{{
				Op:   identitydomains.OperationsOpRemove,
				Path: &path,
			}},
		},
	})
	if err != nil {
		return fmt.Errorf("remove user from administrators group: %w", err)
	}
	return nil
}

// toDomainTenantUser 把 SCIM 用户转为列表通用形状（ID 用对应的 IAM OCID）。
func toDomainTenantUser(u identitydomains.User) TenantUser {
	out := TenantUser{
		ID:             deref(u.Ocid),
		Name:           deref(u.UserName),
		Description:    deref(u.Description),
		LifecycleState: "ACTIVE",
	}
	if out.ID == "" {
		out.ID = deref(u.Id)
	}
	for _, e := range u.Emails {
		if e.Primary != nil && *e.Primary {
			out.Email = deref(e.Value)
		}
	}
	return out
}

// UpdateTenantUserInput 是编辑用户资料的输入；nil 字段不修改。
// 管理员字段为三态：nil 不动，true 授予/加入，false 撤销/移出。
type UpdateTenantUserInput struct {
	Description      *string
	Email            *string
	GivenName        *string
	FamilyName       *string
	GrantDomainAdmin *bool // 「身份域管理员」应用角色
	AddToAdminGroup  *bool // Administrators 组成员
}

// UpdateTenantUser 实现 Client：编辑用户资料（须发往 home region）。
// 优先 Identity Domains SCIM（支持姓名）；用户不在域中时回退经典
// IAM API（仅备注与邮箱，姓名变更报错）;domainID 非空时不回退。
func (c *RealClient) UpdateTenantUser(ctx context.Context, cred Credentials, homeRegion, domainID, userID string, in UpdateTenantUserInput) (TenantUser, error) {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return TenantUser{}, err
	}
	domainUser, found, err := domainUserByOcid(ctx, dc, userID)
	if err != nil {
		return TenantUser{}, err
	}
	if found {
		user, err := patchDomainUser(ctx, dc, domainUser, in)
		if err != nil {
			return user, err
		}
		scimID := deref(domainUser.Id)
		if err := applyAdminGrants(ctx, dc, scimID, in); err != nil {
			return user, err
		}
		return user, nil
	}
	if domainID != "" {
		return TenantUser{}, fmt.Errorf("update user %s: user not found in identity domain", userID)
	}
	if in.GivenName != nil || in.FamilyName != nil {
		return TenantUser{}, fmt.Errorf("update user %s: 姓名仅身份域用户可修改（该用户不在身份域中）", userID)
	}
	if in.GrantDomainAdmin != nil || in.AddToAdminGroup != nil {
		return TenantUser{}, fmt.Errorf("update user %s: 管理员授权仅身份域用户可操作（该用户不在身份域中）", userID)
	}
	return c.updateUserClassic(ctx, cred, homeRegion, userID, in)
}

// updateUserClassic 走经典 IAM API 更新备注与邮箱。
func (c *RealClient) updateUserClassic(ctx context.Context, cred Credentials, homeRegion, userID string, in UpdateTenantUserInput) (TenantUser, error) {
	ic, err := c.identityClientAt(cred, homeRegion)
	if err != nil {
		return TenantUser{}, err
	}
	resp, err := ic.UpdateUser(ctx, identity.UpdateUserRequest{
		UserId:            &userID,
		UpdateUserDetails: identity.UpdateUserDetails{Description: in.Description, Email: in.Email},
	})
	if err != nil {
		return TenantUser{}, fmt.Errorf("update user %s: %w", userID, err)
	}
	return toTenantUser(resp.User, cred.UserOCID), nil
}

// patchDomainUser 以 SCIM Patch 修改域用户的指定字段。
func patchDomainUser(ctx context.Context, dc identitydomains.IdentityDomainsClient, u identitydomains.User, in UpdateTenantUserInput) (TenantUser, error) {
	ops := buildUserPatchOps(u, in)
	if len(ops) == 0 {
		return toDomainTenantUser(u), nil
	}
	resp, err := dc.PatchUser(ctx, identitydomains.PatchUserRequest{
		UserId: u.Id,
		PatchOp: identitydomains.PatchOp{
			Schemas:    []string{"urn:ietf:params:scim:api:messages:2.0:PatchOp"},
			Operations: ops,
		},
	})
	if err != nil {
		return TenantUser{}, fmt.Errorf("patch domain user %s: %w", deref(u.UserName), err)
	}
	return toDomainTenantUser(resp.User), nil
}

// applyAdminGrants 同步管理员状态：nil 不动，true 授予/加入，false 撤销/移出。
func applyAdminGrants(ctx context.Context, dc identitydomains.IdentityDomainsClient, scimID string, in UpdateTenantUserInput) error {
	if in.GrantDomainAdmin != nil {
		apply := revokeDomainAdminRole
		if *in.GrantDomainAdmin {
			apply = grantDomainAdminRole
		}
		if err := apply(ctx, dc, scimID); err != nil {
			return err
		}
	}
	if in.AddToAdminGroup != nil {
		apply := removeUserFromAdminGroup
		if *in.AddToAdminGroup {
			apply = addUserToAdminGroup
		}
		return apply(ctx, dc, scimID)
	}
	return nil
}

func buildUserPatchOps(u identitydomains.User, in UpdateTenantUserInput) []identitydomains.Operations {
	var ops []identitydomains.Operations
	if in.GivenName != nil {
		ops = append(ops, replaceOp("name.givenName", *in.GivenName))
	}
	if in.FamilyName != nil {
		ops = append(ops, replaceOp("name.familyName", *in.FamilyName))
	}
	if in.Description != nil {
		ops = append(ops, replaceOp("description", *in.Description))
	}
	if in.Email != nil {
		ops = append(ops, emailPatchOp(u, *in.Email))
	}
	return ops
}

// emailPatchOp 生成主邮箱修改操作：已有 work 邮箱按 filter 路径替换，
// 没有则整体添加一条 primary work 邮箱（避免整组 replace 冲掉 recovery 邮箱）。
func emailPatchOp(u identitydomains.User, email string) identitydomains.Operations {
	for _, e := range u.Emails {
		if e.Type == identitydomains.UserEmailsTypeWork {
			return replaceOp(`emails[type eq "work"].value`, email)
		}
	}
	var value interface{} = []map[string]interface{}{
		{"value": email, "type": "work", "primary": true},
	}
	return identitydomains.Operations{
		Op:    identitydomains.OperationsOpAdd,
		Path:  common.String("emails"),
		Value: &value,
	}
}

// DeleteTenantUser 实现 Client：删除 IAM 用户（须发往 home region）。
// domainID 非空时经该域 SCIM 删除(经典 API 对非默认域用户不生效)。
func (c *RealClient) DeleteTenantUser(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) error {
	if domainID != "" {
		return c.deleteDomainUser(ctx, cred, homeRegion, domainID, userID)
	}
	ic, err := c.identityClientAt(cred, homeRegion)
	if err != nil {
		return err
	}
	if _, err := ic.DeleteUser(ctx, identity.DeleteUserRequest{UserId: &userID}); err != nil {
		return fmt.Errorf("delete user %s: %w", userID, err)
	}
	return nil
}

// deleteDomainUser 经 SCIM 强制删除域用户(forceDelete 连带其授权与组成员关系)。
func (c *RealClient) deleteDomainUser(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) error {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return err
	}
	scimID, err := scimUserID(ctx, dc, userID)
	if err != nil {
		return err
	}
	force := true
	_, err = dc.DeleteUser(ctx, identitydomains.DeleteUserRequest{UserId: &scimID, ForceDelete: &force})
	if err != nil {
		return fmt.Errorf("delete domain user %s: %w", userID, err)
	}
	return nil
}

// ResetTenantUserPassword 实现 Client：重置用户控制台密码并返回一次性新密码。
// domainID 非空直走该域 SCIM;为空优先经典 IAM API,
// 新式租户（用户只存在于 Identity Domain）自动回退 SCIM。
func (c *RealClient) ResetTenantUserPassword(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) (string, error) {
	if domainID != "" {
		return c.resetPasswordViaDomain(ctx, cred, homeRegion, domainID, userID)
	}
	ic, err := c.identityClientAt(cred, homeRegion)
	if err != nil {
		return "", err
	}
	resp, err := ic.CreateOrResetUIPassword(ctx, identity.CreateOrResetUIPasswordRequest{UserId: &userID})
	if err == nil {
		return deref(resp.Password), nil
	}
	if !isClassicUnsupported(err) {
		return "", fmt.Errorf("reset ui password of %s: %w", userID, err)
	}
	return c.resetPasswordViaDomain(ctx, cred, homeRegion, "", userID)
}

// resetPasswordViaDomain 通过 Identity Domains SCIM API 强设随机新密码。
func (c *RealClient) resetPasswordViaDomain(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) (string, error) {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return "", err
	}
	scimID, err := scimUserID(ctx, dc, userID)
	if err != nil {
		return "", err
	}
	password, err := generatePassword()
	if err != nil {
		return "", err
	}
	bypass := true
	_, err = dc.PutUserPasswordChanger(ctx, identitydomains.PutUserPasswordChangerRequest{
		UserPasswordChangerId: &scimID,
		UserPasswordChanger: identitydomains.UserPasswordChanger{
			Schemas:            []string{"urn:ietf:params:scim:schemas:oracle:idcs:UserPasswordChanger"},
			Password:           &password,
			BypassNotification: &bypass,
		},
	})
	if err != nil {
		// 身份域通常不允许凭据本人经管理通道改自己的密码
		if userID == cred.UserOCID {
			return "", fmt.Errorf("change password via identity domain（当前签名用户请在 OCI 控制台修改密码）: %w", err)
		}
		return "", fmt.Errorf("change password via identity domain: %w", err)
	}
	return password, nil
}

// DeleteTenantUserMfaDevices 实现 Client：清除用户全部 MFA。
// 先删经典 TOTP 设备，再通过 Identity Domains 移除全部认证因子（尽力）。
func (c *RealClient) DeleteTenantUserMfaDevices(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) (int, error) {
	// 显式指定域时用户的认证因子只在域内,经典 TOTP 清理不适用
	if domainID != "" {
		return 0, c.removeDomainFactors(ctx, cred, homeRegion, domainID, userID)
	}
	ic, err := c.identityClientAt(cred, homeRegion)
	if err != nil {
		return 0, err
	}
	deleted, err := deleteClassicTotpDevices(ctx, ic, userID)
	if err != nil {
		return deleted, err
	}
	if err := c.removeDomainFactors(ctx, cred, homeRegion, "", userID); err != nil {
		return deleted, err
	}
	return deleted, nil
}

func deleteClassicTotpDevices(ctx context.Context, ic identity.IdentityClient, userID string) (int, error) {
	resp, err := ic.ListMfaTotpDevices(ctx, identity.ListMfaTotpDevicesRequest{UserId: &userID})
	if err != nil {
		return 0, fmt.Errorf("list mfa totp devices: %w", err)
	}
	deleted := 0
	for _, item := range resp.Items {
		_, err := ic.DeleteMfaTotpDevice(ctx, identity.DeleteMfaTotpDeviceRequest{
			UserId:          &userID,
			MfaTotpDeviceId: item.Id,
		})
		if err != nil {
			return deleted, fmt.Errorf("delete mfa totp device: %w", err)
		}
		deleted++
	}
	return deleted, nil
}

// removeDomainFactors 通过 SCIM AuthenticationFactorsRemover 移除用户
// 在 Identity Domain 注册的全部认证因子；用户不在域中时视为无事可做。
func (c *RealClient) removeDomainFactors(ctx context.Context, cred Credentials, homeRegion, domainID, userID string) error {
	dc, err := c.domainsClient(ctx, cred, homeRegion, domainID)
	if err != nil {
		return err
	}
	scimID, err := scimUserID(ctx, dc, userID)
	if err != nil {
		return nil
	}
	_, err = dc.CreateAuthenticationFactorsRemover(ctx, identitydomains.CreateAuthenticationFactorsRemoverRequest{
		AuthenticationFactorsRemover: identitydomains.AuthenticationFactorsRemover{
			Schemas: []string{"urn:ietf:params:scim:schemas:oracle:idcs:AuthenticationFactorsRemover"},
			User:    &identitydomains.AuthenticationFactorsRemoverUser{Value: &scimID},
		},
	})
	if err != nil {
		return fmt.Errorf("remove authentication factors: %w", err)
	}
	return nil
}

// DeleteTenantUserApiKeys 实现 Client：删除用户的 API Key。
// 默认跳过当前配置正在使用的指纹以免面板失联，includeCurrent 为 true 时一并删除。
func (c *RealClient) DeleteTenantUserApiKeys(ctx context.Context, cred Credentials, homeRegion, userID string, includeCurrent bool) (int, error) {
	ic, err := c.identityClientAt(cred, homeRegion)
	if err != nil {
		return 0, err
	}
	resp, err := ic.ListApiKeys(ctx, identity.ListApiKeysRequest{UserId: &userID})
	if err != nil {
		return 0, fmt.Errorf("list api keys: %w", err)
	}
	deleted := 0
	for _, key := range resp.Items {
		fingerprint := deref(key.Fingerprint)
		if !includeCurrent && userID == cred.UserOCID && fingerprint == cred.Fingerprint {
			continue
		}
		_, err := ic.DeleteApiKey(ctx, identity.DeleteApiKeyRequest{UserId: &userID, Fingerprint: &fingerprint})
		if err != nil {
			return deleted, fmt.Errorf("delete api key %s: %w", fingerprint, err)
		}
		deleted++
	}
	return deleted, nil
}

// scimUserID 用经典 OCID 在 Identity Domain 中解析 SCIM 用户 ID。
func scimUserID(ctx context.Context, dc identitydomains.IdentityDomainsClient, classicOCID string) (string, error) {
	u, found, err := domainUserByOcid(ctx, dc, classicOCID)
	if err != nil {
		return "", err
	}
	if !found {
		return "", fmt.Errorf("user %s not found in identity domain", classicOCID)
	}
	return *u.Id, nil
}

// domainUserByOcid 用经典 OCID 查域用户；不存在时 found 为 false 而非报错。
// extraAttrs 用于按需追加 groups、appRoles 等重量级属性。
func domainUserByOcid(ctx context.Context, dc identitydomains.IdentityDomainsClient, classicOCID string, extraAttrs ...string) (identitydomains.User, bool, error) {
	filter := fmt.Sprintf("ocid eq %q", classicOCID)
	attrs := strings.Join(append([]string{"id,ocid,userName,description,name,emails"}, extraAttrs...), ",")
	count := 1
	resp, err := dc.ListUsers(ctx, identitydomains.ListUsersRequest{
		Filter:     &filter,
		Attributes: &attrs,
		Count:      &count,
	})
	if err != nil {
		return identitydomains.User{}, false, fmt.Errorf("find domain user by ocid: %w", err)
	}
	if len(resp.Resources) == 0 || resp.Resources[0].Id == nil {
		return identitydomains.User{}, false, nil
	}
	return resp.Resources[0], true, nil
}

// isClassicUnsupported 判断经典 IAM API 对该用户不适用（仅存在于 Identity Domain）。
func isClassicUnsupported(err error) bool {
	var svcErr common.ServiceError
	if errors.As(err, &svcErr) {
		return svcErr.GetHTTPStatusCode() == 404 || svcErr.GetHTTPStatusCode() == 400
	}
	return false
}

// passwordCharsets 保证生成的密码覆盖四类字符，满足常见复杂度策略。
var passwordCharsets = []string{
	"ABCDEFGHJKLMNPQRSTUVWXYZ",
	"abcdefghijkmnpqrstuvwxyz",
	"23456789",
	"#-_+!",
}

// generatePassword 生成 16 位随机密码，每类字符至少出现一次。
func generatePassword() (string, error) {
	const length = 16
	all := strings.Join(passwordCharsets, "")
	out := make([]byte, 0, length)
	for _, set := range passwordCharsets {
		ch, err := randomChar(set)
		if err != nil {
			return "", err
		}
		out = append(out, ch)
	}
	for len(out) < length {
		ch, err := randomChar(all)
		if err != nil {
			return "", err
		}
		out = append(out, ch)
	}
	return string(out), nil
}

func randomChar(set string) (byte, error) {
	n, err := rand.Int(rand.Reader, big.NewInt(int64(len(set))))
	if err != nil {
		return 0, fmt.Errorf("generate password: %w", err)
	}
	return set[n.Int64()], nil
}

func toTenantUser(u identity.User, currentUserOCID string) TenantUser {
	return TenantUser{
		ID:             deref(u.Id),
		Name:           deref(u.Name),
		Description:    deref(u.Description),
		Email:          deref(u.Email),
		EmailVerified:  u.EmailVerified != nil && *u.EmailVerified,
		MfaActivated:   u.IsMfaActivated != nil && *u.IsMfaActivated,
		LifecycleState: string(u.LifecycleState),
		IsCurrentUser:  deref(u.Id) == currentUserOCID,
		TimeCreated:    sdkTime(u.TimeCreated),
		LastLoginTime:  sdkTime(u.LastSuccessfulLoginTime),
	}
}
