import re
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple


def extract_season_episode(file_path_or_name: str) -> Tuple[Optional[int], Optional[int]]:
    """
    Extracts season and episode numbers from a video filename or full path.
    Supports all popular naming conventions:
      - s01e01, S01E01, s1e1, S1E1, s1e01, S1E01
      - S01.E01, S01 - E01, S01_E01
      - 1x01, 01x01, 1x1, 02x15
      - Season 1 Episode 1, 1. Évad 2. Rész, 1.evad.02.resz
      - Folder matching: .../Season 2/04 - Episode.mkv
    Returns (season, episode) or (None, None).
    """
    if not file_path_or_name:
        return None, None

    path = Path(file_path_or_name)
    stem = path.stem

    # 1. Standard SxxExx: e.g. s01e01, S1E1, s1e01, S01.E05, S02_E10, S01 - E02
    m = re.search(r"[sS](\d{1,3})[\s._-]*[eE](\d{1,4})", stem)
    if m:
        return int(m.group(1)), int(m.group(2))

    # 2. NxNN: e.g. 1x01, 02x15, 1x1
    m = re.search(r"(?:^|[\s._\-\(\[])(\d{1,2})[xX](\d{1,4})(?:[\s._\-\)\]]|$)", stem)
    if m:
        return int(m.group(1)), int(m.group(2))

    # 3. Hungarian word order: "1. évad 03. rész", "1.evad 03.resz", "1 evad 2 resz"
    m = re.search(
        r"(\d{1,3})\.?[\s._-]*(?:évad|evad|szezon|season)[\s._-]*(\d{1,4})\.?[\s._-]*(?:rész|resz|epizod|epizód|episode)",
        stem,
        re.IGNORECASE,
    )
    if m:
        return int(m.group(1)), int(m.group(2))

    # 4. English / standard word order: "Season 1 Episode 2", "Evad 1 Resz 2"
    m = re.search(
        r"(?:season|szezon|évad|evad)[\s._-]*(\d{1,3})[\s._-]*(?:episode|epizod|epizód|rész|resz)[\s._-]*(\d{1,4})",
        stem,
        re.IGNORECASE,
    )
    if m:
        return int(m.group(1)), int(m.group(2))

    # 5. Separate season and episode anywhere in stem
    m_season = re.search(
        r"(?:(?:[sS]|season|szezon|évad|evad)[\s._-]*(\d{1,3})|(\d{1,3})\.?[\s._-]*(?:évad|evad|szezon))",
        stem,
        re.IGNORECASE,
    )
    s_val = None
    if m_season:
        s_val = int(m_season.group(1) or m_season.group(2))

    m_ep = re.search(
        r"(?:(?:[eE]|ep|episode|epizod|epizód|rész|resz)[\s._-]*(\d{1,4})|(\d{1,4})\.?[\s._-]*(?:rész|resz|epizod|epizód))",
        stem,
        re.IGNORECASE,
    )
    e_val = None
    if m_ep:
        e_val = int(m_ep.group(1) or m_ep.group(2))

    if s_val is not None and e_val is not None:
        return s_val, e_val

    # 6. Check parent directory for season if only episode is in filename
    # e.g. .../Season 2/05 - Title.mkv or .../S02/Ep05.mkv or .../Season 4/07.mkv
    parent_name = path.parent.name if path.parent else ""
    m_parent_season = re.search(
        r"(?:(?:[sS]|season|szezon|évad|evad)[\s._-]*(\d{1,3})|(\d{1,3})\.?[\s._-]*(?:évad|evad|szezon))",
        parent_name,
        re.IGNORECASE,
    )
    parent_s_val = int(m_parent_season.group(1) or m_parent_season.group(2)) if m_parent_season else None

    if parent_s_val is not None and e_val is not None:
        return parent_s_val, e_val

    # 7. Parent has season, filename has just episode number (e.g. "07 - The Massacre.mkv" or "01.mkv")
    if parent_s_val is not None:
        m_num = re.search(r"(?:^|[\s._\-])(\d{1,3})(?:[\s._\-]|$)", stem)
        if m_num:
            return parent_s_val, int(m_num.group(1))

    # 8. Standalone episode without season (e.g. "ep05.mp4" or "05.resz.mp4") -> defaults to Season 1
    if e_val is not None:
        return 1, e_val

    return None, None


def scan_directory_for_episodes(folder_path: str) -> List[Dict[str, Any]]:
    """
    Scans a directory recursively for video files, extracts season & episode for each,
    and returns a sorted list of episode details.
    """
    path = Path(folder_path)
    if not path.is_dir():
        return []

    video_exts = {".mp4", ".mkv", ".avi", ".mov", ".wmv", ".flv", ".webm", ".m4v", ".ts"}
    episodes = []

    for file_path in path.rglob("*"):
        if file_path.is_file() and file_path.suffix.lower() in video_exts:
            s, e = extract_season_episode(str(file_path))
            episodes.append({
                "file_path": str(file_path.resolve()),
                "file_name": file_path.name,
                "file_size": file_path.stat().st_size,
                "season": s,
                "episode": e,
            })

    # Sort episodes by (season or 999, episode or 999, file_name)
    episodes.sort(key=lambda x: (x["season"] if x["season"] is not None else 999, x["episode"] if x["episode"] is not None else 999, x["file_name"]))
    return episodes
