from huaweicloudsdkcore.auth.credentials import BasicCredentials  # 导入华为云 SDK 鉴权对象。
from huaweicloudsdkcore.region.region import Region  # 导入区域对象用于绑定 VPC 端点。
from huaweicloudsdkvpc.v2 import VpcClient, ShowSubnetRequest  # 导入 VPC 客户端与子网查询模型。
from huaweicloudsdkvpc.v2 import NeutronCreatePortRequest, NeutronCreatePortRequestBody, NeutronCreatePortOption  # 导入虚拟 IP 端口创建模型。
from huaweicloudsdkvpc.v2 import NeutronListPortsRequest, NeutronUpdatePortRequest, NeutronUpdatePortRequestBody, NeutronUpdatePortOption  # 导入端口查询和更新模型。
from huaweicloudsdkvpc.v2 import FixedIp, AllowedAddressPair  # 导入固定 IP 与允许地址对模型。
from config import PROJECT_ID, get_runtime_ak_sk  # 导入鉴权参数。
from iam_service import get_project_id  # 导入项目 ID 自动解析。
from data import VPC_ENDPOINT_TEMPLATE, LOG_ENABLED, ECS_ALLOWED_ADDRESS_PAIR_IP  # 导入 VPC 端点和源目检查配置。
from retry_utils import retry_cloud_call  # 导入云 API 重试工具。


def _resolve_project_id(region_id):  # 根据配置或 IAM 自动解析项目 ID。
    if PROJECT_ID and PROJECT_ID != "YOUR_PROJECT_ID":  # 若已手工配置项目 ID 则直接使用。
        return PROJECT_ID
    return get_project_id(region_id)


def _build_vpc_client(region_id):  # 创建 VPC v2 客户端。
    project_id = _resolve_project_id(region_id)
    ak, sk = get_runtime_ak_sk()
    creds = BasicCredentials(ak, sk).with_project_id(project_id)
    endpoint = VPC_ENDPOINT_TEMPLATE.format(region_id=region_id)
    region = Region(region_id, endpoint)
    return VpcClient.new_builder().with_credentials(creds).with_region(region).build()


def _subnet_network_id(client, subnet_id):  # 虚拟 IP 创建需要 neutron network_id，不能直接使用 virsubnet_id。
    response = retry_cloud_call("查询子网网络ID", lambda: client.show_subnet(ShowSubnetRequest(subnet_id=subnet_id)))
    subnet = response.subnet
    return getattr(subnet, "neutron_network_id", "") or getattr(subnet, "network_id", "") or subnet_id


def _expected_virtual_ips(spec):  # 根据 20 规格生成 192.168.0.12 和 192.168.0.13。
    count = int(spec.get("virtual_ip_count", 0) or 0)
    if count <= 0:
        return []
    main_ip = spec.get("main_nic_ip") or "192.168.0.10"
    prefix = ".".join(main_ip.split(".")[:-1]) + "."
    start = int(spec.get("virtual_ip_start", 0) or 0)
    end = int(spec.get("virtual_ip_end", 0) or 0)
    if start <= 0 or end < start:
        raise ValueError("virtual_ip_start/virtual_ip_end 配置无效")
    return [f"{prefix}{suffix}" for suffix in range(start, end + 1)][:count]


def _port_id(port):  # 兼容 SDK 对象和字典端口结构。
    return getattr(port, "id", "") or (port.get("id", "") if isinstance(port, dict) else "")


def _port_owner(port):  # 读取端口归属类型。
    return (getattr(port, "device_owner", "") or (port.get("device_owner", "") if isinstance(port, dict) else "") or "").lower()


def _port_device_id(port):  # 读取端口绑定的设备 ID。
    return getattr(port, "device_id", "") or (port.get("device_id", "") if isinstance(port, dict) else "")


def _port_fixed_ips(port):  # 读取端口固定私网 IP。
    fixed_ips = getattr(port, "fixed_ips", None) or (port.get("fixed_ips", []) if isinstance(port, dict) else [])
    values = []
    for item in fixed_ips or []:
        ip_address = getattr(item, "ip_address", "") or (item.get("ip_address", "") if isinstance(item, dict) else "")
        if ip_address:
            values.append(ip_address)
    return values


def _port_allowed_pairs(port):  # 读取并标准化 allowed_address_pairs，避免更新时覆盖旧值。
    pairs = getattr(port, "allowed_address_pairs", None) or (port.get("allowed_address_pairs", []) if isinstance(port, dict) else [])
    result = []
    seen_ips = set()
    for item in pairs or []:
        ip_address = getattr(item, "ip_address", "") or (item.get("ip_address", "") if isinstance(item, dict) else "")
        mac_address = getattr(item, "mac_address", "") or (item.get("mac_address", "") if isinstance(item, dict) else "")
        key = str(ip_address or "").strip().lower()
        if key and key not in seen_ips:
            seen_ips.add(key)
            result.append(AllowedAddressPair(ip_address=ip_address, mac_address=mac_address or None))
    return result


def _merge_allowed_pairs(port, additions):  # 合并允许地址对，用于虚拟 IP 绑定和主网卡关闭源目检查。
    merged = []
    seen = set()
    for item in [*_port_allowed_pairs(port), *additions]:
        ip_address = getattr(item, "ip_address", "")
        mac_address = getattr(item, "mac_address", "") or ""
        if not ip_address:
            continue
        # 华为云会把同一个 IP、空 MAC 的地址对视为重复；20 模式重跑时按 IP 去重，避免旧资源反查卡在 DuplicateAddressPair。
        key = str(ip_address).strip().lower()
        if key in seen:
            continue
        seen.add(key)
        merged.append(AllowedAddressPair(ip_address=ip_address, mac_address=mac_address or None))
    return merged


def _pair_signature(pairs):  # 生成地址对签名，用于判断是否需要更新端口。
    return {
        (
            str(getattr(item, "ip_address", "") or (item.get("ip_address", "") if isinstance(item, dict) else "")).strip().lower(),
            str(getattr(item, "mac_address", "") or (item.get("mac_address", "") if isinstance(item, dict) else "")).strip().lower(),
        )
        for item in pairs or []
        if str(getattr(item, "ip_address", "") or (item.get("ip_address", "") if isinstance(item, dict) else "")).strip()
    }


def _has_required_pair_ips(port, required_ips):  # 判断端口是否已经包含需要的 allowed_address_pairs IP。
    existing_ips = {str(getattr(item, "ip_address", "")).strip().lower() for item in _port_allowed_pairs(port)}
    return all(str(ip).strip().lower() in existing_ips for ip in required_ips if str(ip).strip())


def _duplicate_allowed_pair_error(exc):  # 判断华为云重复 allowed_address_pairs 错误，重跑旧资源时可幂等跳过。
    text = str(exc).lower()
    return "duplicateaddresspair" in text or "duplicate address pair" in text or "request contains duplicate address pair" in text


def _find_port_by_id(client, network_id, port_id):  # 更新失败后刷新端口，用于确认云端是否已经写入地址对。
    for port in _list_ports(client, network_id):
        if _port_id(port) == port_id:
            return port
    return None


def _update_allowed_pairs(client, network_id, port_id, port, pairs, required_ips):  # 更新端口允许地址对，重复写入时按成功处理。
    current_pairs = _port_allowed_pairs(port)
    if _pair_signature(current_pairs) == _pair_signature(pairs):
        return port
    request = NeutronUpdatePortRequest(port_id=port_id)
    request.body = NeutronUpdatePortRequestBody(port=NeutronUpdatePortOption(allowed_address_pairs=pairs))
    try:
        response = retry_cloud_call("更新端口允许地址对", lambda: client.neutron_update_port(request))
        return getattr(response, "port", None) or port
    except Exception as exc:
        if _duplicate_allowed_pair_error(exc):
            refreshed = _find_port_by_id(client, network_id, port_id)
            if refreshed and _has_required_pair_ips(refreshed, required_ips):
                if LOG_ENABLED:
                    print(f"步骤: 端口 {port_id} allowed_address_pairs 已存在，跳过重复更新。")
                return refreshed
        raise


def _create_vip_port(client, network_id, security_group_id, private_ip):  # 创建 neutron:VIP_PORT 虚拟 IP 端口。
    option = NeutronCreatePortOption(
        network_id=network_id,
        fixed_ips=[FixedIp(ip_address=private_ip)],
        device_owner="neutron:VIP_PORT",
        name=f"wawacloud-vip-{private_ip.rsplit('.', 1)[-1]}",
        admin_state_up=True,
    )
    if security_group_id:
        option.security_groups = [security_group_id]
    request = NeutronCreatePortRequest()
    request.body = NeutronCreatePortRequestBody(port=option)
    response = retry_cloud_call("创建虚拟IP", lambda: client.neutron_create_port(request))
    return response.port


def _list_ports(client, network_id):  # 查询子网对应网络下的端口。
    request = NeutronListPortsRequest(network_id=network_id, limit=2000)
    response = retry_cloud_call("查询端口列表", lambda: client.neutron_list_ports(request))
    return list(response.ports or [])


def create_virtual_ips(region_id, subnet_id, security_group_id, spec, parent_port_id, parent_private_ip):  # 创建/复用 20 规格虚拟 IP。
    expected_ips = _expected_virtual_ips(spec)
    if not expected_ips:
        return []
    client = _build_vpc_client(region_id)
    network_id = _subnet_network_id(client, subnet_id)
    ports = _list_ports(client, network_id)
    ports_by_id = {_port_id(port): port for port in ports if _port_id(port)}
    vip_by_ip = {}
    for port in ports:
        owner = _port_owner(port)
        device_id = _port_device_id(port)
        for ip_address in _port_fixed_ips(port):
            if ip_address not in expected_ips:
                continue
            if "vip" in owner or not device_id:
                vip_by_ip[ip_address] = port
            else:
                raise RuntimeError(f"虚拟IP {ip_address} 已被非VIP端口占用: {_port_id(port)}")

    created_ids = []
    for ip_address in expected_ips:
        port = vip_by_ip.get(ip_address)
        if not port:
            port = _create_vip_port(client, network_id, security_group_id, ip_address)
            if LOG_ENABLED:
                print(f"步骤: 创建虚拟IP {ip_address} port={_port_id(port)}")
            vip_by_ip[ip_address] = port
            ports_by_id[_port_id(port)] = port
        vip_pairs = _merge_allowed_pairs(port, [AllowedAddressPair(ip_address=parent_private_ip)])
        port = _update_allowed_pairs(client, network_id, _port_id(port), port, vip_pairs, [parent_private_ip])
        created_ids.append(_port_id(port))

    parent_port = ports_by_id.get(parent_port_id, {"id": parent_port_id, "allowed_address_pairs": []})
    source_pairs = _merge_allowed_pairs(parent_port, [AllowedAddressPair(ip_address=ECS_ALLOWED_ADDRESS_PAIR_IP)])
    _update_allowed_pairs(client, network_id, parent_port_id, parent_port, source_pairs, [ECS_ALLOWED_ADDRESS_PAIR_IP])
    if LOG_ENABLED:
        print(f"步骤: 虚拟IP完成 {','.join(expected_ips)} -> 主网卡 {parent_private_ip}")
    return created_ids
