from __future__ import annotations

import codecs
import os
import sys
from pathlib import Path
from typing import Tuple


def _idna_encode(value: str, errors: str = "strict") -> Tuple[bytes, int]:
    """给打包壳体缺少 encodings.idna 时兜底；云接口域名都是 ASCII。"""
    text = str(value or "")
    try:
        return text.encode("ascii"), len(text)
    except UnicodeEncodeError as exc:
        if errors == "ignore":
            return text.encode("ascii", errors="ignore"), len(text)
        if errors == "replace":
            return text.encode("ascii", errors="replace"), len(text)
        raise UnicodeError("当前便携壳体缺少完整 idna 编码，且域名包含非 ASCII 字符。") from exc


def _idna_decode(value: bytes | bytearray | memoryview | str, errors: str = "strict") -> Tuple[str, int]:
    """ASCII 域名解码兜底，避免 urllib/socket 因 idna 编码缺失直接失败。"""
    if isinstance(value, str):
        return value, len(value)
    raw = bytes(value or b"")
    return raw.decode("ascii", errors=errors), len(raw)


class _IdnaIncrementalEncoder(codecs.IncrementalEncoder):
    def encode(self, input: str, final: bool = False) -> bytes:
        return _idna_encode(input, self.errors)[0]


class _IdnaIncrementalDecoder(codecs.IncrementalDecoder):
    def decode(self, input: bytes, final: bool = False) -> str:
        return _idna_decode(input, self.errors)[0]


class _IdnaStreamWriter(codecs.StreamWriter):
    encode = staticmethod(_idna_encode)


class _IdnaStreamReader(codecs.StreamReader):
    decode = staticmethod(_idna_decode)


def _has_file(path_text: str, filename: str) -> bool:
    """检查环境变量里的路径是否还能找到目标文件。"""
    if not path_text:
        return False
    try:
        return (Path(path_text) / filename).exists()
    except OSError:
        return False


def _path_inside(path_text: str, root_text: str) -> bool:
    """判断路径是否在指定根目录下，用来识别过期 _MEI 路径。"""
    if not path_text or not root_text:
        return False
    try:
        Path(path_text).resolve().relative_to(Path(root_text).resolve())
        return True
    except (OSError, ValueError):
        return False


def _looks_like_pyinstaller_temp(path_text: str) -> bool:
    """识别 PyInstaller onefile 解包出来的 _MEI 临时目录。"""
    try:
        return any(part.upper().startswith("_MEI") for part in Path(path_text).parts)
    except OSError:
        return False


def _candidate_tcl_tk_pairs() -> list[tuple[Path, Path]]:
    """按稳定优先级寻找 Tcl/Tk 运行库，避免旧壳体启动时找不到 init.tcl。"""
    pairs: list[tuple[Path, Path]] = []
    meipass = getattr(sys, "_MEIPASS", "")
    if meipass:
        bundle = Path(meipass)
        pairs.extend(
            [
                (bundle / "_tcl_data", bundle / "_tk_data"),
                (bundle / "tcl" / "tcl8.6", bundle / "tcl" / "tk8.6"),
                (bundle / "tcl8" / "tcl8.6", bundle / "tcl8" / "tk8.6"),
            ]
        )

    portable_root = os.environ.get("WAWA_PORTABLE_ROOT", "").strip()
    if portable_root:
        root = Path(portable_root)
        pairs.append((root / "tcl" / "tcl8.6", root / "tcl" / "tk8.6"))

    # 源码运行或本机 Python 存在时，用 Python 自带 Tcl/Tk 作为可靠兜底。
    for prefix_name in ("base_prefix", "prefix"):
        prefix = getattr(sys, prefix_name, "")
        if prefix:
            root = Path(prefix)
            pairs.append((root / "tcl" / "tcl8.6", root / "tcl" / "tk8.6"))

    return pairs


def ensure_tcl_tk_environment() -> None:
    """在导入 tkinter 前修正 Tcl/Tk 路径，减少打包 exe 的启动弹窗。"""
    current_tcl = os.environ.get("TCL_LIBRARY", "")
    current_tk = os.environ.get("TK_LIBRARY", "")
    if _has_file(current_tcl, "init.tcl") and _has_file(current_tk, "tk.tcl"):
        current_meipass = getattr(sys, "_MEIPASS", "")
        if not current_meipass or not (_looks_like_pyinstaller_temp(current_tcl) or _looks_like_pyinstaller_temp(current_tk)):
            return
        if _path_inside(current_tcl, current_meipass) and _path_inside(current_tk, current_meipass):
            return

    for tcl_dir, tk_dir in _candidate_tcl_tk_pairs():
        if (tcl_dir / "init.tcl").exists() and (tk_dir / "tk.tcl").exists():
            os.environ["TCL_LIBRARY"] = str(tcl_dir)
            os.environ["TK_LIBRARY"] = str(tk_dir)
            return


def ensure_idna_codec() -> None:
    """注册启动兜底；旧壳体只会调用这个入口，所以 Tcl/Tk 也在这里处理。"""
    ensure_tcl_tk_environment()
    try:
        codecs.lookup("idna")
        return
    except LookupError:
        pass

    def search(encoding: str):
        normalized = encoding.replace("-", "_").lower()
        if normalized != "idna":
            return None
        return codecs.CodecInfo(
            name="idna",
            encode=_idna_encode,
            decode=_idna_decode,
            incrementalencoder=_IdnaIncrementalEncoder,
            incrementaldecoder=_IdnaIncrementalDecoder,
            streamwriter=_IdnaStreamWriter,
            streamreader=_IdnaStreamReader,
        )

    codecs.register(search)
