# -*- coding: utf-8 -*-
import os
import time
from typing import Any, Dict, List, Optional, Set, Tuple

import volcenginesdkcore
import volcenginesdkecs
import volcenginesdkvpc
from volcenginesdkcore.rest import ApiException

from volcengine import config


def _normalize(value: Optional[str]) -> str:
    return (value or "").strip()


def _resolve_credentials(ak: Optional[str], sk: Optional[str]) -> Tuple[str, str]:
    ak_value = _normalize(ak) or _normalize(os.getenv("VOLC_AK")) or _normalize(getattr(config, "DEFAULT_ACCESS_KEY", ""))
    sk_value = _normalize(sk) or _normalize(os.getenv("VOLC_SK")) or _normalize(getattr(config, "DEFAULT_SECRET_KEY", ""))
    return ak_value, sk_value


def _resolve_region(region: Optional[str]) -> str:
    return _normalize(region) or _normalize(getattr(config, "DEFAULT_REGION", ""))


def _build_configuration(ak: str, sk: str, region: str) -> volcenginesdkcore.Configuration:
    configuration = volcenginesdkcore.Configuration()
    configuration.ak = ak
    configuration.sk = sk
    configuration.region = region
    return configuration


def _to_dict(obj) -> Dict:
    if obj is None:
        return {}
    if isinstance(obj, dict):
        return obj
    to_dict = getattr(obj, "to_dict", None)
    return to_dict() if callable(to_dict) else {}


def _request(cls, **kwargs):
    try:
        return cls(**kwargs)
    except TypeError:
        request = cls()
        for key, value in kwargs.items():
            try:
                setattr(request, key, value)
            except Exception:
                pass
        return request


def _result(data: Dict[str, Any]) -> Dict[str, Any]:
    result = data.get("Result") or data.get("result")
    return result if isinstance(result, dict) else data


def _field(data: Dict[str, Any], *keys: str) -> str:
    for key in keys:
        value = data.get(key)
        if value not in (None, ""):
            return str(value).strip()
    raw = data.get("_raw")
    if isinstance(raw, dict):
        for key in keys:
            value = raw.get(key)
            if value not in (None, ""):
                return str(value).strip()
    return ""


def _key_name(value: str) -> str:
    return str(value or "").replace("_", "").replace("-", "").lower()


def _collect_values_by_key(value: Any, key_names: Set[str]) -> List[str]:
    found: List[str] = []
    normalized_names = {_key_name(item) for item in key_names}
    if isinstance(value, dict):
        for key, item in value.items():
            if _key_name(key) in normalized_names:
                if isinstance(item, list):
                    found.extend(str(entry).strip() for entry in item if entry not in (None, ""))
                elif item not in (None, "") and not isinstance(item, dict):
                    found.append(str(item).strip())
            found.extend(_collect_values_by_key(item, normalized_names))
    elif isinstance(value, list):
        for item in value:
            found.extend(_collect_values_by_key(item, normalized_names))
    unique: List[str] = []
    seen: Set[str] = set()
    for item in found:
        if item and item not in seen:
            seen.add(item)
            unique.append(item)
    return unique


def _extract_list(data: Dict[str, Any], *keys: str) -> List[Dict[str, Any]]:
    preferred = {_key_name(key) for key in keys}

    def walk(value: Any) -> List[Dict[str, Any]]:
        if isinstance(value, dict):
            for key, item in value.items():
                if _key_name(key) in preferred and isinstance(item, list):
                    return [entry for entry in item if isinstance(entry, dict)]
            for item in value.values():
                found = walk(item)
                if found:
                    return found
        elif isinstance(value, list):
            dict_items = [entry for entry in value if isinstance(entry, dict)]
            if dict_items and any(
                entry.get("NetworkInterfaceId")
                or entry.get("network_interface_id")
                or entry.get("InstanceId")
                or entry.get("instance_id")
                for entry in dict_items
            ):
                return dict_items
            for item in value:
                found = walk(item)
                if found:
                    return found
        return []

    return walk(data)


def _format_api_exception(exc: ApiException) -> str:
    parts: List[str] = []
    status = getattr(exc, "status", None)
    reason = getattr(exc, "reason", None)
    body = getattr(exc, "body", None)
    if status:
        parts.append(f"status={status}")
    if reason:
        parts.append(f"reason={reason}")
    if body:
        parts.append(f"body={body}")
    return "; ".join(parts) if parts else str(exc)


def _is_absent_error(error_msg: str) -> bool:
    lowered = (error_msg or "").lower()
    return any(
        token in lowered
        for token in (
            "notfound",
            "not found",
            "invalidnetworkinterface.notfound",
            "invalidinstance.notfound",
            "invalidallocationid.notfound",
            "invalidassociation.notfound",
        )
    )


def _is_dependency_or_retryable_error(error_msg: str) -> bool:
    lowered = (error_msg or "").lower()
    return any(
        token in lowered
        for token in (
            "inuse",
            "in use",
            "depend",
            "dependency",
            "invalidstatus",
            "invalidoperation.conflict",
            "operationdenied",
            "conflict",
            "throttl",
            "timeout",
            "serviceunavailable",
            "internalerror",
        )
    )


def _network_interface_name(nic: Dict[str, Any]) -> str:
    return _field(nic, "NetworkInterfaceName", "network_interface_name", "Name", "name") or "未命名网卡"


def _network_interface_id(nic: Dict[str, Any]) -> str:
    return _field(nic, "NetworkInterfaceId", "network_interface_id", "EniId", "eni_id", "id")


def _network_interface_attached_instance_id(nic: Dict[str, Any]) -> str:
    return _field(
        nic,
        "InstanceId",
        "instance_id",
        "AttachedInstanceId",
        "attached_instance_id",
        "AttachedInstanceID",
    )


def _network_interface_private_ips(nic: Dict[str, Any]) -> List[str]:
    return _collect_values_by_key(
        nic,
        {
            "PrivateIpAddress",
            "private_ip_address",
            "PrimaryIpAddress",
            "primary_ip_address",
            "PrivateIp",
            "private_ip",
            "IpAddress",
            "ip_address",
        },
    )


def _network_interface_vpc_ids(nic: Dict[str, Any]) -> List[str]:
    return _collect_values_by_key(nic, {"VpcId", "vpc_id"})


def _network_interface_subnet_ids(nic: Dict[str, Any]) -> List[str]:
    return _collect_values_by_key(nic, {"SubnetId", "subnet_id"})


def describe_network_interfaces(
    region: Optional[str],
    ak: Optional[str] = None,
    sk: Optional[str] = None,
) -> Tuple[bool, str, List[Dict[str, Any]]]:
    ak_value, sk_value = _resolve_credentials(ak, sk)
    region_value = _resolve_region(region)
    if not ak_value or not sk_value:
        return False, "AK/SK 为空，无法查询网卡", []
    if not region_value:
        return False, "地区为空，无法查询网卡", []
    volcenginesdkcore.Configuration.set_default(_build_configuration(ak_value, sk_value, region_value))
    api_instance = volcenginesdkvpc.VPCApi()

    all_items: List[Dict[str, Any]] = []
    seen: Set[str] = set()
    for page in range(1, 21):
        request = _request(volcenginesdkvpc.DescribeNetworkInterfacesRequest, page_number=page, page_size=100)
        try:
            response = api_instance.describe_network_interfaces(request)
        except ApiException as exc:
            return False, f"查询网卡失败: {_format_api_exception(exc)}", []
        data_dict = _to_dict(response)
        items = _extract_list(
            data_dict,
            "NetworkInterfaceSets",
            "network_interface_sets",
            "NetworkInterfaceSet",
            "NetworkInterfaces",
            "network_interfaces",
        )
        for item in items:
            nic_id = _network_interface_id(item)
            key = nic_id or repr(sorted(item.items()))
            if key in seen:
                continue
            seen.add(key)
            all_items.append(item)
        total = _result(data_dict).get("TotalCount") or _result(data_dict).get("total_count") or _result(data_dict).get("Total")
        try:
            total_int = int(total)
        except (TypeError, ValueError):
            total_int = 0
        if not items or (total_int and len(all_items) >= total_int):
            break
    return True, "查询网卡成功", all_items


def _active_instance_exists(
    region: Optional[str],
    instance_id: str,
    ak: Optional[str],
    sk: Optional[str],
) -> Tuple[Optional[bool], str]:
    instance_id = _normalize(instance_id)
    if not instance_id:
        return False, "网卡未绑定实例"
    ak_value, sk_value = _resolve_credentials(ak, sk)
    region_value = _resolve_region(region)
    if not ak_value or not sk_value or not region_value:
        return None, "AK/SK 或地区为空，无法确认网卡绑定实例是否存在"
    volcenginesdkcore.Configuration.set_default(_build_configuration(ak_value, sk_value, region_value))
    api_instance = volcenginesdkecs.ECSApi()
    for page in range(1, 21):
        request = _request(volcenginesdkecs.DescribeInstancesRequest, page_number=page, page_size=100)
        try:
            response = api_instance.describe_instances(request)
        except ApiException as exc:
            return None, f"查询绑定实例失败: {_format_api_exception(exc)}"
        data_dict = _to_dict(response)
        items = _extract_list(data_dict, "Instances", "InstanceSet", "instances", "instance_set")
        for item in items:
            current_id = _field(item, "InstanceId", "instance_id", "id")
            if current_id != instance_id:
                continue
            status = str(_field(item, "Status", "status")).strip().lower()
            if status in {"deleted", "deleting"}:
                return False, "绑定实例已处于删除状态"
            return True, "绑定实例仍存在"
        total = _result(data_dict).get("TotalCount") or _result(data_dict).get("total_count") or _result(data_dict).get("Total")
        try:
            total_int = int(total)
        except (TypeError, ValueError):
            total_int = 0
        if not items or (total_int and page * 100 >= total_int):
            break
    return False, "未查询到绑定实例"


def _nic_matches_fixed_ip_target(nic: Dict[str, Any], vpc_id: str, subnet_id: str, target_ips: Set[str]) -> Tuple[bool, List[str]]:
    nic_vpcs = set(_network_interface_vpc_ids(nic))
    nic_subnets = set(_network_interface_subnet_ids(nic))
    if vpc_id and (not nic_vpcs or vpc_id not in nic_vpcs):
        return False, []
    if subnet_id and (not nic_subnets or subnet_id not in nic_subnets):
        return False, []
    matched_ips = [ip for ip in _network_interface_private_ips(nic) if ip in target_ips]
    return bool(matched_ips), matched_ips


def _disassociate_eip(api_instance, allocation_id: str, max_retries: int = 4) -> Tuple[bool, str]:
    if not allocation_id:
        return True, "EIP 为空，跳过解绑"
    request = _request(volcenginesdkvpc.DisassociateEipAddressRequest, allocation_id=allocation_id)
    last_error = ""
    for attempt in range(1, max_retries + 1):
        try:
            api_instance.disassociate_eip_address(request)
            return True, "EIP 解绑成功"
        except ApiException as exc:
            error_msg = _format_api_exception(exc)
            lowered = error_msg.lower()
            if _is_absent_error(error_msg) or "not associated" in lowered or "notassociate" in lowered or "invalidassociation" in lowered:
                return True, "EIP 已未绑定"
            last_error = error_msg
            if attempt < max_retries and _is_dependency_or_retryable_error(error_msg):
                time.sleep(min(2 * attempt, 6))
                continue
            return False, "EIP 解绑失败"
    return False, last_error or "EIP 解绑失败"


def _detach_network_interface(api_instance, network_interface_id: str, instance_id: str, max_retries: int = 4) -> Tuple[bool, str]:
    if not network_interface_id or not instance_id:
        return True, "网卡无需解绑实例"
    request = _request(
        volcenginesdkvpc.DetachNetworkInterfaceRequest,
        network_interface_id=network_interface_id,
        instance_id=instance_id,
    )
    for attempt in range(1, max_retries + 1):
        try:
            api_instance.detach_network_interface(request)
            time.sleep(2)
            return True, "网卡解绑成功"
        except ApiException as exc:
            error_msg = _format_api_exception(exc)
            lowered = error_msg.lower()
            if _is_absent_error(error_msg) or "not attached" in lowered or "not associated" in lowered or "invalidattachment" in lowered:
                return True, "网卡已未绑定实例"
            if "primary" in lowered and ("detach" in lowered or "not support" in lowered or "not allowed" in lowered or "cannot" in lowered):
                return True, "主网卡不支持单独解绑，继续尝试删除"
            if attempt < max_retries and _is_dependency_or_retryable_error(error_msg):
                time.sleep(min(2 * attempt, 6))
                continue
            return False, "网卡解绑失败"
    return False, "网卡解绑失败"


def _delete_network_interface(api_instance, network_interface_id: str, max_retries: int = 8) -> Tuple[bool, str]:
    if not network_interface_id:
        return False, "网卡 ID 为空，无法删除"
    request = _request(volcenginesdkvpc.DeleteNetworkInterfaceRequest, network_interface_id=network_interface_id)
    for attempt in range(1, max_retries + 1):
        try:
            api_instance.delete_network_interface(request)
            return True, "网卡删除成功"
        except ApiException as exc:
            error_msg = _format_api_exception(exc)
            if _is_absent_error(error_msg):
                return True, "网卡已不存在"
            if attempt < max_retries and _is_dependency_or_retryable_error(error_msg):
                time.sleep(min(2 * attempt, 10))
                continue
            return False, "网卡删除失败"
    return False, "网卡删除失败"


def _disassociate_eips_for_nic(
    region: Optional[str],
    api_instance,
    network_interface_id: str,
    matched_private_ips: Set[str],
    ak: Optional[str],
    sk: Optional[str],
) -> Tuple[bool, str, int]:
    try:
        from volcengine.eip_service import describe_eip_addresses
    except Exception as exc:
        return False, f"加载 EIP 查询失败: {exc}", 0
    ok, message, eips = describe_eip_addresses(region, ak=ak, sk=sk)
    if not ok:
        return False, message, 0
    count = 0
    for eip in eips:
        allocation_id = _field(eip, "AllocationId", "allocation_id", "EipId", "eip_id", "id")
        associated_id = _field(
            eip,
            "AssociatedInstanceId",
            "associated_instance_id",
            "AssociatedInstanceID",
            "InstanceId",
            "instance_id",
            "NetworkInterfaceId",
            "network_interface_id",
            "EniId",
            "eni_id",
        )
        private_ip = _field(eip, "PrivateIpAddress", "private_ip_address", "PrivateIp", "private_ip", "AssociatedPrivateIpAddress", "associated_private_ip_address")
        if not allocation_id:
            continue
        if associated_id != network_interface_id and (not private_ip or private_ip not in matched_private_ips):
            continue
        disassociate_ok, disassociate_message = _disassociate_eip(api_instance, allocation_id)
        if not disassociate_ok:
            return False, disassociate_message, count
        count += 1
    return True, "EIP 解绑检查完成", count


def cleanup_orphan_network_interfaces_by_private_ips(
    region: Optional[str],
    vpc_id: str,
    subnet_id: str,
    private_ips: Optional[List[str]],
    ak: Optional[str] = None,
    sk: Optional[str] = None,
    wait_timeout: int = 60,
    wait_interval: int = 3,
) -> Tuple[bool, str, Dict[str, Any]]:
    target_ips = set(_normalize_ip_list(private_ips))
    if not target_ips:
        return True, "未配置固定私网 IP，跳过残留网卡检查", {"deleted_count": 0, "deleted_names": []}
    if not vpc_id or not subnet_id:
        return True, "VPC 或子网为空，跳过残留网卡检查", {"deleted_count": 0, "deleted_names": []}
    ak_value, sk_value = _resolve_credentials(ak, sk)
    region_value = _resolve_region(region)
    if not ak_value or not sk_value:
        return False, "AK/SK 为空，无法清理残留网卡", {"deleted_count": 0, "deleted_names": []}
    if not region_value:
        return False, "地区为空，无法清理残留网卡", {"deleted_count": 0, "deleted_names": []}

    ok, message, nics = describe_network_interfaces(region_value, ak=ak_value, sk=sk_value)
    if not ok:
        return False, message, {"deleted_count": 0, "deleted_names": []}
    candidates: List[Tuple[Dict[str, Any], List[str]]] = []
    for nic in nics:
        matches, matched_ips = _nic_matches_fixed_ip_target(nic, vpc_id, subnet_id, target_ips)
        if matches:
            candidates.append((nic, matched_ips))
    if not candidates:
        return True, "未发现占用固定私网 IP 的残留网卡", {"deleted_count": 0, "deleted_names": []}

    volcenginesdkcore.Configuration.set_default(_build_configuration(ak_value, sk_value, region_value))
    api_instance = volcenginesdkvpc.VPCApi()
    deleted_ids: Set[str] = set()
    deleted_names: List[str] = []
    blocked_names: List[str] = []
    eip_disassociated_count = 0

    for nic, matched_ips in candidates:
        nic_id = _network_interface_id(nic)
        nic_name = _network_interface_name(nic)
        attached_instance_id = _network_interface_attached_instance_id(nic)
        if not nic_id:
            blocked_names.append(nic_name)
            continue
        if attached_instance_id:
            exists, exists_message = _active_instance_exists(region_value, attached_instance_id, ak_value, sk_value)
            if exists is True:
                blocked_names.append(nic_name)
                continue
            if exists is None:
                return False, f"无法确认残留网卡是否仍绑定有效 ECS，已停止自动清理: {nic_name}，{exists_message}", {
                    "deleted_count": len(deleted_ids),
                    "deleted_names": deleted_names,
                    "blocked_names": blocked_names,
                    "eip_disassociated_count": eip_disassociated_count,
                }
            detach_ok, detach_message = _detach_network_interface(api_instance, nic_id, attached_instance_id)
            if not detach_ok:
                return False, f"残留网卡解绑失败: {nic_name}，{detach_message}", {
                    "deleted_count": len(deleted_ids),
                    "deleted_names": deleted_names,
                    "blocked_names": blocked_names,
                    "eip_disassociated_count": eip_disassociated_count,
                }
        eip_ok, eip_message, eip_count = _disassociate_eips_for_nic(
            region_value,
            api_instance,
            nic_id,
            set(matched_ips),
            ak_value,
            sk_value,
        )
        if not eip_ok:
            return False, f"残留网卡 EIP 解绑失败: {nic_name}，{eip_message}", {
                "deleted_count": len(deleted_ids),
                "deleted_names": deleted_names,
                "blocked_names": blocked_names,
                "eip_disassociated_count": eip_disassociated_count,
            }
        eip_disassociated_count += eip_count
        delete_ok, delete_message = _delete_network_interface(api_instance, nic_id)
        if not delete_ok:
            return False, f"残留网卡删除失败: {nic_name}，{delete_message}", {
                "deleted_count": len(deleted_ids),
                "deleted_names": deleted_names,
                "blocked_names": blocked_names,
                "eip_disassociated_count": eip_disassociated_count,
            }
        deleted_ids.add(nic_id)
        deleted_names.append(nic_name)

    if blocked_names:
        return False, f"固定私网 IP 仍被有效 ECS 网卡占用，未自动删除: {', '.join(blocked_names)}", {
            "deleted_count": len(deleted_ids),
            "deleted_names": deleted_names,
            "blocked_names": blocked_names,
            "eip_disassociated_count": eip_disassociated_count,
        }

    deadline = time.time() + max(1, wait_timeout)
    while time.time() < deadline:
        ok, message, current_nics = describe_network_interfaces(region_value, ak=ak_value, sk=sk_value)
        if not ok:
            time.sleep(wait_interval)
            continue
        still_exists = False
        for nic in current_nics:
            nic_id = _network_interface_id(nic)
            matches, _ = _nic_matches_fixed_ip_target(nic, vpc_id, subnet_id, target_ips)
            if nic_id in deleted_ids or matches:
                still_exists = True
                break
        if not still_exists:
            return True, "残留网卡已清理完成", {
                "deleted_count": len(deleted_ids),
                "deleted_names": deleted_names,
                "blocked_names": [],
                "eip_disassociated_count": eip_disassociated_count,
            }
        time.sleep(wait_interval)

    return False, "残留网卡删除已提交，但云端仍返回固定私网 IP 占用，稍后重试", {
        "deleted_count": len(deleted_ids),
        "deleted_names": deleted_names,
        "blocked_names": [],
        "eip_disassociated_count": eip_disassociated_count,
    }


def _normalize_ip_list(private_ips: Optional[List[str]]) -> List[str]:
    if not private_ips:
        return []
    if isinstance(private_ips, str):
        private_ips = [private_ips]
    return [ip for ip in (_normalize(item) for item in private_ips) if ip]


def _is_ip_already_bound_error(error_msg: str) -> bool:
    lowered = error_msg.lower()
    return (
        "invalidipaddress.inuse" in lowered
        or "specified ip address is in use" in lowered
        or "already in use" in lowered
        or "ip address is in use" in lowered
    )


def assign_private_ips(
    region: Optional[str],
    network_interface_id: str,
    private_ips: Optional[List[str]],
    ak: Optional[str] = None,
    sk: Optional[str] = None,
    max_retries: int = 6,
    retry_interval: int = 3,
) -> Tuple[bool, str, Dict]:
    ak_value, sk_value = _resolve_credentials(ak, sk)
    region_value = _resolve_region(region)
    if not ak_value or not sk_value:
        return False, "AK/SK 为空，无法绑定私网 IP", {}
    if not region_value:
        return False, "地域为空，无法绑定私网 IP", {}
    if not network_interface_id:
        return False, "NetworkInterfaceId 为空，无法绑定私网 IP", {}
    ip_list = _normalize_ip_list(private_ips)
    if not ip_list:
        return True, "未提供需要绑定的私网 IP，跳过", {}

    volcenginesdkcore.Configuration.set_default(_build_configuration(ak_value, sk_value, region_value))
    api_instance = volcenginesdkvpc.VPCApi()
    request = volcenginesdkvpc.AssignPrivateIpAddressesRequest(
        network_interface_id=network_interface_id,
        private_ip_address=ip_list,
    )
    for attempt in range(1, max_retries + 1):
        try:
            response = api_instance.assign_private_ip_addresses(request)
            return True, "私网 IP 绑定成功", _to_dict(response)
        except ApiException as exc:
            error_msg = str(exc)
            if _is_ip_already_bound_error(error_msg):
                return True, "私网 IP 已存在，跳过重复绑定", {}
            if "InvalidNetworkInterface.InvalidStatus" in error_msg or "InvalidInstance.InvalidStatus" in error_msg:
                if attempt < max_retries:
                    time.sleep(retry_interval)
                    continue
            return False, f"私网 IP 绑定失败: {exc}", {}
    return False, f"私网 IP 绑定超时({max_retries}次重试)", {}
