"""Shared helpers: path sanitisation, URL normalisation, duration parsing, size parsing."""

from __future__ import annotations

import re
from pathlib import Path

# Multipliers for parse_size()
_SIZE_UNITS: dict[str, int] = {
    "B": 1,
    "KB": 10**3, "KIB": 2**10,
    "MB": 10**6, "MIB": 2**20,
    "GB": 10**9, "GIB": 2**30,
    "TB": 10**12, "TIB": 2**40,
    "PB": 10**15, "PIB": 2**50,
}


def parse_size(s: str) -> int:
    """Parse a human-readable size string (e.g. '500GB', '1.5TiB') to bytes."""
    s = s.strip()
    m = re.match(r"^([0-9]+(?:\.[0-9]+)?)\s*([A-Za-z]+)$", s)
    if not m:
        raise ValueError(f"Invalid size string: {s!r}")
    value = float(m.group(1))
    unit = m.group(2).upper()
    if unit not in _SIZE_UNITS:
        raise ValueError(f"Unknown size unit: {m.group(2)!r}. Use one of: {', '.join(sorted(_SIZE_UNITS))}")
    return int(value * _SIZE_UNITS[unit])


def sanitise_path_component(name: str) -> str:
    """Remove or replace characters unsafe for filesystem paths."""
    name = name.strip()
    name = re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", name)
    name = re.sub(r"_+", "_", name)
    return name.strip("_") or "unknown"


def normalise_url(url: str) -> str:
    """Prepend https: to protocol-relative URLs."""
    if url.startswith("//"):
        return f"https:{url}"
    return url


def parse_duration(length_str: str) -> float:
    """Parse xeno-canto duration string 'm:ss' to seconds."""
    if not length_str:
        return 0.0
    parts = length_str.strip().split(":")
    if len(parts) == 2:
        try:
            return int(parts[0]) * 60 + int(parts[1])
        except ValueError:
            return 0.0
    if len(parts) == 3:
        try:
            return int(parts[0]) * 3600 + int(parts[1]) * 60 + int(parts[2])
        except ValueError:
            return 0.0
    return 0.0


def build_relative_dir(family: str, genus: str, species: str) -> Path:
    """Build the Family/Genus/Genus_Species relative directory path."""
    family = sanitise_path_component(family) if family else "Unknown_Family"
    genus = sanitise_path_component(genus) if genus else "Unknown_Genus"
    species_epithet = sanitise_path_component(species) if species else "unknown_sp"
    species_dir = f"{genus}_{species_epithet}"
    return Path(family) / genus / species_dir


def build_filename(xc_id: str, genus: str, species: str, ext: str) -> str:
    """Build XC<id>_Genus_species.ext filename."""
    genus = sanitise_path_component(genus) if genus else "Unknown"
    species_epithet = sanitise_path_component(species) if species else "sp"
    return f"XC{xc_id.zfill(8)}_{genus}_{species_epithet}.{ext}"


def original_ext_from_filename(filename: str) -> str:
    """Extract extension from the original xeno-canto filename."""
    if "." in filename:
        return filename.rsplit(".", 1)[-1].lower()
    return "mp3"
