#!/usr/bin/env python3
"""Resolve Heltec USB CDC (Espressif 303a) and P3 FTDI (/dev/pogcc) ports."""

from __future__ import annotations

import glob
import os
from typing import List, Optional


HELTEC_VID = "303a"
P3_POGCC = "/dev/pogcc"
P3_FALLBACK = "/dev/ttyUSB0"
P3_PHYS = "/dev/ser_DT1"


def _read_text(path: str) -> str:
    try:
        with open(path, encoding="utf-8", errors="replace") as handle:
            return handle.read().strip().lower()
    except OSError:
        return ""


def list_heltec_acm_nodes() -> List[str]:
    """Return ttyACM paths whose USB parent VID is Espressif (303a)."""
    found: List[str] = []
    for node in sorted(glob.glob("/dev/ttyACM*")):
        base = os.path.basename(node)
        sys_tty = f"/sys/class/tty/{base}/device"
        try:
            usb = os.path.realpath(sys_tty)
        except OSError:
            continue
        # Walk up looking for idVendor
        cur = usb
        for _ in range(8):
            vid = _read_text(os.path.join(cur, "idVendor"))
            if vid == HELTEC_VID:
                found.append(node)
                break
            parent = os.path.dirname(cur)
            if parent == cur:
                break
            cur = parent
    # by-id fallback (names vary)
    for link in sorted(glob.glob("/dev/serial/by-id/*")):
        low = link.lower()
        if "303a" not in low and "espressif" not in low and "heltec" not in low:
            continue
        try:
            target = os.path.realpath(link)
        except OSError:
            continue
        if target.startswith("/dev/ttyACM") and target not in found:
            found.append(target)
    return found


def resolve_heltec_cdc_port() -> Optional[str]:
    """Prefer the last ACM for dual-CDC boards (app CDC often ACM1)."""
    nodes = list_heltec_acm_nodes()
    if not nodes:
        # Last resort: single ACM present
        acms = sorted(glob.glob("/dev/ttyACM*"))
        if len(acms) == 1:
            return acms[0]
        return None
    return nodes[-1]


def resolve_p3_port() -> str:
    if os.path.exists(P3_POGCC):
        return P3_POGCC
    if os.path.exists(P3_PHYS):
        return P3_PHYS
    return P3_FALLBACK
