"""
Scraper / parser for ktunotes.live subject data.

Parses the JavaScript data files from the ktunotes-next source tree
to extract subject codes, names, and Google Drive folder links.
"""

import re
import os
from typing import Dict, Optional


# ─── Default path to the ktunotes-next data directory ────────────────────────
_DEFAULT_DATA_DIR = os.path.join(
    os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
    "ktunotes1", "ktunotes-next", "app", "semesters"
)

_DATA_FILES = {
    "S1": "s1/data.js",
    "S2": "s2/data.js",
    "S3": "s3/data.js",
    "S4": "s4/s4Data.js",
}

# Branches we recognize in S3/S4 data
_BRANCHES = ["cse", "it", "ece", "eee", "mech", "civil", "chemical"]


def _find_subject_objects(text: str) -> list[str]:
    """
    Extract individual subject objects from the JS file by matching
    balanced braces around blocks that contain a 'title' field.
    Returns list of object text strings.
    """
    objects = []
    i = 0
    while i < len(text):
        # Find the start of an object that has title inside
        brace_pos = text.find('{', i)
        if brace_pos == -1:
            break

        # Track depth to find matching close brace
        depth = 1
        j = brace_pos + 1
        while j < len(text) and depth > 0:
            if text[j] == '{':
                depth += 1
            elif text[j] == '}':
                depth -= 1
            j += 1

        block = text[brace_pos:j]

        # Only include blocks that have title and buttons (subject entries)
        if 'title' in block and 'buttons' in block:
            # Make sure it's a leaf subject (not a container)
            # Leaf subjects have buttons with href
            if 'href' in block:
                objects.append(block)

        i = brace_pos + 1

    return objects


def _extract_field(block: str, field: str) -> str:
    """Extract a string field from a JS object."""
    pattern = re.compile(
        rf'{field}\s*:\s*["\'](.+?)["\']',
        re.DOTALL
    )
    match = pattern.search(block)
    return match.group(1).strip() if match else ""


def _extract_code_from_title(title: str) -> str:
    """Extract code like GAMAT401 from 'Title Name (GAMAT401)'."""
    match = re.search(r'\(([A-Z]{2,6}\d{2,4})\)', title)
    return match.group(1).lower() if match else ""


def _extract_gdrive_link(block: str) -> str:
    """
    Find the Google Drive folder link from Modules/Lab Manual buttons.
    """
    # Look for buttons labeled Modules or Lab Manual specifically
    # These are multi-line patterns, so we scan for label then href nearby
    module_pattern = re.compile(
        r'label\s*:\s*["\'](?:Modules|Lab Manual)["\']'
        r'.*?'
        r'href\s*:\s*["\']'
        r'(https://drive\.google\.com/drive/folders/[^"\']+)'
        r'["\']',
        re.DOTALL
    )
    match = module_pattern.search(block)
    if match:
        return match.group(1)

    # Fallback: any drive.google.com/drive/folders link
    fallback = re.search(
        r'href\s*:\s*["\']'
        r'(https://drive\.google\.com/drive/folders/[^"\']+)'
        r'["\']',
        block
    )
    return fallback.group(1) if fallback else ""


def _detect_branch(block: str, full_text: str, semester: str) -> str:
    """Detect the branch context for a subject."""
    if semester in ("S1", "S2"):
        return "Common"

    pos = full_text.find(block[:80])
    if pos == -1:
        return "All"

    preceding = full_text[:pos]
    # Find the nearest branch key before this block
    branch_matches = list(re.finditer(
        r'\b(' + '|'.join(_BRANCHES) + r')\s*[:{]',
        preceding, re.IGNORECASE
    ))
    return branch_matches[-1].group(1).upper() if branch_matches else "All"


def _make_code(title: str) -> str:
    """Generate a slug code from the title as last resort."""
    slug = re.sub(r'[^a-z0-9]+', '_', title.lower()).strip('_')
    return slug[:25] if slug else "unknown"


def parse_data_file(filepath: str, semester: str) -> Dict[str, dict]:
    """Parse a single JS data file and return subjects keyed by code."""
    subjects = {}

    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
    except (FileNotFoundError, IOError):
        return subjects

    objects = _find_subject_objects(content)

    for block in objects:
        title = _extract_field(block, 'title')
        if not title or len(title) < 3:
            continue

        # Skip non-subject entries
        if any(skip in title.upper() for skip in [
            "PREVIOUS YEAR", "NASSCOM", "DIGITAL 101"
        ]):
            continue

        # Determine subject code (3 strategies)
        code = _extract_field(block, 'code').lower()  # Explicit code field
        if not code:
            code = _extract_code_from_title(title)     # Code in parentheses
        if not code:
            code = _make_code(title)                   # Slug fallback

        gdrive = _extract_gdrive_link(block)
        description = _extract_field(block, 'description')
        branch = _detect_branch(block, content, semester)

        # Clean title — remove code in parentheses if present
        clean_title = re.sub(r'\s*\([A-Z]{2,6}\d{2,4}\)\s*', '', title).strip()

        subjects[code] = {
            "name": clean_title or title,
            "semester": semester,
            "branch": branch,
            "gdrive": gdrive,
            "description": description,
        }

    return subjects


def scrape_all(data_dir: Optional[str] = None) -> Dict[str, dict]:
    """
    Parse all semester data files and return a unified subject dict.

    Returns dict keyed by lowercase subject code, e.g.:
    {
        "gamat401": {
            "name": "Mathematics for Computer and Information Science - 4",
            "semester": "S4",
            "branch": "CSE",
            "gdrive": "https://drive.google.com/drive/folders/...",
            "description": "..."
        }
    }
    """
    if data_dir is None:
        data_dir = _DEFAULT_DATA_DIR

    all_subjects = {}

    for semester, rel_path in _DATA_FILES.items():
        filepath = os.path.join(data_dir, rel_path)
        if os.path.exists(filepath):
            semester_subjects = parse_data_file(filepath, semester)
            for code, data in semester_subjects.items():
                if code not in all_subjects:
                    all_subjects[code] = data

    return all_subjects
