"""
MkDocs Hook for Wiki-Link Resolution

Converts Obsidian-style [[wiki-links]] to standard Markdown links/images.

Supported syntax:
  [[page-name]]              → [page-name](../page-name/)
  [[page-name|Display Text]] → [Display Text](../page-name/)
  [[page-name#heading]]      → [page-name](../page-name/#heading)
  ![[image.png]]             → ![image.png](../path/to/image.png)
  ![[image.png|alt text]]    → ![alt text](../path/to/image.png)

Links inside fenced code blocks and inline code are skipped.
Unresolved links are left as-is with a warning logged.
"""

import os
import re
import logging
from typing import Dict, Any, Optional

logger = logging.getLogger(__name__)

# Module-level file index: filename (lowercase) → relative path from docs root
_file_index: Dict[str, str] = {}

# Whether directory URLs are enabled
_use_directory_urls: bool = True

# Image extensions
_IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp', '.tiff', '.avif'}


def on_files(files: Any, config: Dict[str, Any]) -> None:
    """Build a file index mapping lowercased filenames to their paths."""
    global _file_index, _use_directory_urls
    _file_index.clear()
    _use_directory_urls = config.get('use_directory_urls', True)

    for f in files:
        # f.src_path is relative to docs_dir, e.g. "docs/getting-started/installation.md"
        src = f.src_path
        basename = os.path.basename(src)
        # For .md files, also index without extension
        name_lower = basename.lower()
        _file_index[name_lower] = src

        if name_lower.endswith('.md'):
            stem = name_lower[:-3]
            # Don't overwrite if stem already exists (first wins)
            if stem not in _file_index:
                _file_index[stem] = src

    logger.info(f"[wikilinks] Indexed {len(_file_index)} files for wiki-link resolution")


def _resolve_link(name: str) -> Optional[str]:
    """Look up a filename in the index (case-insensitive). Returns src_path or None."""
    key = name.lower().strip()
    return _file_index.get(key)


def _compute_relative_path(from_page_src: str, to_src: str, anchor: str = '') -> str:
    """Compute relative URL from one page to another, accounting for directory URLs."""
    from_dir = os.path.dirname(from_page_src)

    _, ext = os.path.splitext(to_src)
    is_image = ext.lower() in _IMAGE_EXTENSIONS
    is_md = ext.lower() == '.md'

    if is_image:
        # Images: direct relative path to the file
        rel = os.path.relpath(to_src, from_dir)
        return rel.replace(os.sep, '/')

    if is_md and _use_directory_urls:
        # With directory URLs, pages become page-name/index.html
        # So we link to the directory (without .md)
        page_dir = to_src[:-3]  # strip .md
        # Handle index.md → links to the directory itself
        if os.path.basename(to_src).lower() == 'index.md':
            page_dir = os.path.dirname(to_src)
        rel = os.path.relpath(page_dir, from_dir)
        url = rel.replace(os.sep, '/') + '/'
    elif is_md:
        # Without directory URLs, link directly to .md (MkDocs converts to .html)
        rel = os.path.relpath(to_src, from_dir)
        url = rel.replace(os.sep, '/').replace('.md', '.html')
    else:
        # Other files: direct path
        rel = os.path.relpath(to_src, from_dir)
        url = rel.replace(os.sep, '/')

    if anchor:
        url += '#' + anchor

    return url


# Regex to match wiki-links: optional ! prefix, then [[content]]
_WIKILINK_RE = re.compile(r'(!?)\[\[([^\]]+)\]\]')


def _replace_wikilinks(markdown: str, page_src: str) -> str:
    """Replace wiki-links in markdown, skipping code blocks."""
    lines = markdown.split('\n')
    result_lines = []
    in_fenced_block = False

    for line in lines:
        # Track fenced code blocks (``` or ~~~)
        stripped = line.lstrip()
        if stripped.startswith('```') or stripped.startswith('~~~'):
            in_fenced_block = not in_fenced_block
            result_lines.append(line)
            continue

        if in_fenced_block:
            result_lines.append(line)
            continue

        # Process wiki-links in this line, but skip inline code
        # Strategy: split by inline code spans, only process non-code parts
        parts = re.split(r'(`[^`]+`)', line)
        processed_parts = []
        for part in parts:
            if part.startswith('`') and part.endswith('`'):
                # Inside inline code — leave as-is
                processed_parts.append(part)
            else:
                # Process wiki-links in this segment
                processed_parts.append(_WIKILINK_RE.sub(
                    lambda m: _resolve_wikilink(m, page_src),
                    part
                ))
        result_lines.append(''.join(processed_parts))

    return '\n'.join(result_lines)


def _resolve_wikilink(match: re.Match, page_src: str) -> str:
    """Resolve a single wiki-link match to markdown."""
    is_embed = match.group(1) == '!'
    inner = match.group(2).strip()

    # Parse: name|display and name#anchor
    display = None
    anchor = ''

    if '|' in inner:
        name_part, display = inner.split('|', 1)
        display = display.strip()
        name_part = name_part.strip()
    else:
        name_part = inner

    if '#' in name_part:
        name_part, anchor = name_part.split('#', 1)
        anchor = anchor.strip()

    name_part = name_part.strip()

    # Resolve the target file
    target_src = _resolve_link(name_part)

    if target_src is None:
        # Try with .md appended
        target_src = _resolve_link(name_part + '.md')

    if target_src is None:
        # Try common image extensions
        for ext in ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp']:
            target_src = _resolve_link(name_part + ext)
            if target_src:
                break

    if target_src is None:
        logger.warning(f"[wikilinks] Unresolved wiki-link: [[{inner}]] in {page_src}")
        return match.group(0)  # Leave as-is

    url = _compute_relative_path(page_src, target_src, anchor)

    if is_embed:
        # Image embed: ![[image.png]] or ![[image.png|alt text]]
        alt = display or name_part
        return f'![{alt}]({url})'
    else:
        # Document link: [[page]] or [[page|Display Text]]
        label = display or (name_part + (f'#{anchor}' if anchor else ''))
        return f'[{label}]({url})'


def on_page_markdown(markdown: str, page: Any, config: Dict[str, Any], files: Any) -> str:
    """Process wiki-links in page markdown content."""
    # Quick check — skip pages without wiki-links
    if '[[' not in markdown:
        return markdown

    return _replace_wikilinks(markdown, page.file.src_path)
