from huaweicloudsdkcore.auth.credentials import GlobalCredentials  # 导入华为云 SDK 的全局鉴权对象。 
from huaweicloudsdkcore.region.region import Region  # 导入区域对象用于设置服务端点。 
from huaweicloudsdkiam.v3 import IamClient, KeystoneListProjectsRequest, KeystoneListAuthDomainsRequest, KeystoneListAuthProjectsRequest  # 导入 IAM 客户端与请求模型。 
from config import DOMAIN_ID, get_runtime_ak_sk  # 导入运行时鉴权参数与可选域 ID。 
from data import IAM_ENDPOINT  # 导入 IAM 服务端点配置。 
import json  # 导入 JSON 模块用于读取主程序传入的项目 ID 映射。
import os  # 导入系统模块用于读取环境变量。
from retry_utils import retry_cloud_call  # 导入华为云临时网络异常重试工具。


def _to_plain_dict(value):  # 将 SDK 对象转换为普通字典。 
    if value is None: 
        return {} 
    if isinstance(value, dict): 
        return value 
    to_json_object = getattr(value, "to_json_object", None) 
    if callable(to_json_object): 
        try: 
            data = to_json_object() 
            if isinstance(data, dict): 
                return data 
        except Exception: 
            pass 
    to_dict = getattr(value, "to_dict", None) 
    if callable(to_dict): 
        try: 
            data = to_dict() 
            if isinstance(data, dict): 
                return data 
        except Exception: 
            pass 
    return {} 


def _build_iam_client_for_credentials(ak, sk, domain_id=""):  # 使用指定 AK/SK 创建 IAM 客户端。 
    creds = GlobalCredentials(ak, sk)  # 使用 AK/SK 构造全局鉴权对象。 
    domain_value = domain_id or DOMAIN_ID  # 优先使用调用方传入的域 ID。 
    if domain_value:  # 若用户提供了域 ID，则写入鉴权对象提高成功率。 
        creds = creds.with_domain_id(domain_value)  # 将域 ID 绑定到鉴权对象。 
    region = Region("global", IAM_ENDPOINT)  # 使用全局区域标识与 IAM 端点构造区域对象。 
    client = IamClient.new_builder().with_credentials(creds).with_region(region).build()  # 构建 IAM 客户端实例。 
    return client  # 返回构建好的客户端对象。 


def _build_iam_client():  # 创建 IAM 客户端的函数。 
    ak, sk = get_runtime_ak_sk()  # 读取当前线程生效的 AK/SK。 
    return _build_iam_client_for_credentials(ak, sk)  # 使用当前运行时凭据构建客户端。 


def get_account_identity_for_credentials(ak, sk):  # 通过客户 AK/SK 获取华为云账号身份。 
    client = _build_iam_client_for_credentials(ak, sk)  # 使用客户 AK/SK 构建 IAM 客户端。 
    response = retry_cloud_call("查询账号Domain", lambda: client.keystone_list_auth_domains(KeystoneListAuthDomainsRequest()))  # 查询 AK/SK 所属账号。
    domains = []  # 收集账号域信息。 
    for domain in response.domains or []: 
        data = _to_plain_dict(domain)  # 转换 SDK 对象。 
        account_id = str(data.get("id") or getattr(domain, "id", "") or "").strip() 
        account_name = str(data.get("name") or getattr(domain, "name", "") or "").strip() 
        enabled = data.get("enabled", getattr(domain, "enabled", None)) 
        domains.append({ 
            "account_id": account_id, 
            "account": account_name, 
            "enabled": enabled, 
            "raw": data, 
        }) 
    if not domains: 
        raise RuntimeError("未获取到华为云账号 Domain 信息") 
    primary = domains[0] 
    return { 
        "provider": "huawei", 
        "account_id": primary.get("account_id", ""), 
        "account": primary.get("account", ""), 
        "enabled": primary.get("enabled"), 
        "domains": domains, 
    } 


def _project_name(project):  # 从 SDK 项目对象中提取项目名称。 
    data = _to_plain_dict(project)  # 兼容 SDK 对象和普通字典。 
    return str(data.get("name") or getattr(project, "name", "") or "").strip()  # 返回清洗后的名称。 


def _project_id(project):  # 从 SDK 项目对象中提取项目 ID。 
    data = _to_plain_dict(project)  # 兼容 SDK 对象和普通字典。 
    return str(data.get("id") or getattr(project, "id", "") or "").strip()  # 返回清洗后的 ID。 


def _find_project_id(projects, region_id):  # 在项目列表中按 regionId 匹配项目。 
    for project in projects or []:  # 遍历项目列表。 
        if _project_name(project) == region_id:  # 项目名通常等于 regionId。 
            project_id = _project_id(project)  # 提取项目 ID。 
            if project_id:  # 找到有效 ID 才返回。 
                return project_id  # 返回匹配的项目 ID。 
            raise RuntimeError(f"华为云地区 {region_id} 已返回项目但缺少项目 ID，请检查该地区项目状态。")  # 状态异常时停止。 
    return ""  # 未匹配到项目。 


def get_project_id(region_id):  # 通过区域名自动获取项目 ID 的函数。 
    project_ids_raw = os.getenv("HUAWEI_PROJECT_IDS_JSON", "").strip()  # 主程序已查询到的项目 ID 映射。 
    if project_ids_raw:  # 若主程序已传入项目 ID，则优先使用，避免再次请求 IAM 导致网络断开。 
        try: 
            project_ids = json.loads(project_ids_raw) 
            project_id = str(project_ids.get(region_id) or "").strip() 
            if project_id: 
                return project_id 
        except Exception: 
            pass 
    client = _build_iam_client()  # 构建 IAM 客户端以调用项目列表接口。 
    request = KeystoneListProjectsRequest(name=region_id)  # 先按项目名过滤，项目名通常等于 regionId。 
    response = retry_cloud_call("查询IAM项目", lambda: client.keystone_list_projects(request))  # 调用 IAM 获取项目列表响应。
    project_id = _find_project_id(response.projects, region_id)  # 从过滤结果中提取项目 ID。 
    if project_id:  # 若过滤结果非空则直接使用。 
        return project_id  # 返回匹配到的项目 ID。 
    request_all = KeystoneListProjectsRequest()  # 构造不带过滤条件的请求以兜底。 
    response_all = retry_cloud_call("查询IAM全量项目", lambda: client.keystone_list_projects(request_all))  # 获取全量项目列表用于二次匹配。
    project_id = _find_project_id(response_all.projects, region_id)  # 从全量项目中匹配 regionId。 
    if project_id:  # 若全量列表匹配成功。 
        return project_id  # 返回匹配到的项目 ID。 
    auth_response = retry_cloud_call("查询授权项目", lambda: client.keystone_list_auth_projects(KeystoneListAuthProjectsRequest()))  # 兜底查询当前 IAM 用户可访问项目。
    project_id = _find_project_id(auth_response.projects, region_id)  # 在授权项目列表里匹配 regionId。 
    if project_id:  # 若授权项目匹配成功。 
        return project_id  # 返回匹配到的项目 ID。 
    raise RuntimeError(f"无法通过 IAM 获取地区 {region_id} 的项目 ID。该账号可能还没有初始化这个地区，请先在华为云控制台切换并访问该地区一次，等“我的凭证”项目列表出现后再重试。")  # 全部失败则抛出明确提示。 
