from __future__ import annotations

import hashlib
import hmac
import json
import urllib.parse
from typing import Any, Dict


ALGORITHM = "SDK-HMAC-SHA256"


def json_dumps_bytes(data: Any) -> bytes:
    """按华为云签名要求生成紧凑 UTF-8 JSON 字节。"""
    return json.dumps(data, ensure_ascii=False, separators=(",", ":")).encode("utf-8")


def sha256_hex(payload: bytes) -> str:
    """计算 SHA256 十六进制摘要。"""
    return hashlib.sha256(payload).hexdigest()


def canonical_uri(path: str) -> str:
    """生成华为云签名使用的规范 URI。"""
    if not path:
        path = "/"
    encoded = urllib.parse.quote(path, safe="/-_.~")
    if not encoded.startswith("/"):
        encoded = "/" + encoded
    if not encoded.endswith("/"):
        encoded += "/"
    return encoded


def canonical_query(query: str) -> str:
    """生成华为云签名使用的规范查询字符串。"""
    if not query:
        return ""
    pairs = urllib.parse.parse_qsl(query, keep_blank_values=True)
    encoded_pairs = []
    for key, value in pairs:
        encoded_pairs.append(
            (
                urllib.parse.quote(key, safe="-_.~"),
                urllib.parse.quote(value, safe="-_.~"),
            )
        )
    encoded_pairs.sort()
    return "&".join(f"{key}={value}" for key, value in encoded_pairs)


def sign_headers(
    method: str,
    url: str,
    headers: Dict[str, str],
    payload: bytes,
    ak: str,
    sk: str,
) -> Dict[str, str]:
    """给华为云 OpenAPI 请求头追加 Authorization 签名。"""
    parsed = urllib.parse.urlsplit(url)
    signed_headers_map = {key.lower(): str(value).strip() for key, value in headers.items()}
    signed_headers_map["host"] = parsed.netloc

    signed_header_names = sorted(signed_headers_map)
    canonical_headers = "".join(f"{name}:{signed_headers_map[name]}\n" for name in signed_header_names)
    signed_headers = ";".join(signed_header_names)
    canonical_request = "\n".join(
        [
            method.upper(),
            canonical_uri(parsed.path),
            canonical_query(parsed.query),
            canonical_headers,
            signed_headers,
            sha256_hex(payload),
        ]
    )
    string_to_sign = "\n".join(
        [
            ALGORITHM,
            signed_headers_map["x-sdk-date"],
            sha256_hex(canonical_request.encode("utf-8")),
        ]
    )
    signature = hmac.new(sk.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
    auth = f"{ALGORITHM} Access={ak}, SignedHeaders={signed_headers}, Signature={signature}"

    request_headers = dict(headers)
    request_headers["Host"] = parsed.netloc
    request_headers["Authorization"] = auth
    return request_headers
