"""
Custom Markdown extension for interactive task lists.
Converts - [ ] and - [x] syntax to interactive checkboxes with data-hash attributes.
"""
import re
import hashlib
from markdown.extensions import Extension
from markdown.treeprocessors import Treeprocessor
from xml.etree import ElementTree as etree


def compute_task_hash(task_text):
    """
    Compute a stable hash for a task based on its text content.
    This allows tracking task state even if content is reordered.
    """
    # Normalize whitespace and strip for consistent hashing
    normalized = ' '.join(task_text.strip().split())
    return hashlib.md5(normalized.encode('utf-8')).hexdigest()[:12]


class TaskListTreeprocessor(Treeprocessor):
    """
    Post-process the markdown tree to convert task list items into interactive checkboxes.
    Works with the standard markdown list processing.
    """

    TASK_PATTERN = re.compile(r'^\[([ xX])\]\s+(.*)$')

    def __init__(self, md, task_states=None):
        super().__init__(md)
        self.task_states = task_states or {}

    def run(self, root):
        """Process all list items in the tree."""
        self._process_element(root)
        return root

    def _process_element(self, element):
        """Recursively process elements looking for list items with task syntax."""
        for child in list(element):
            if child.tag == 'li':
                self._process_list_item(child)
            self._process_element(child)

    def _process_list_item(self, li_element):
        """
        Convert list items with [ ] or [x] syntax to interactive checkboxes.

        Original: <li>[ ] Task text</li>
        Result: <li class="task-item"><input type="checkbox" data-hash="abc123"/> Task text</li>
        """
        # Get the text content of the first element (usually a paragraph or direct text)
        if len(li_element) == 0:
            text = li_element.text or ''
        else:
            # Handle case where text is in a paragraph
            first_child = li_element[0]
            if first_child.tag == 'p':
                text = first_child.text or ''
            else:
                text = li_element.text or ''

        # Check if this is a task list item
        match = self.TASK_PATTERN.match(text.strip())
        if not match:
            return

        # Extract checkbox state and task text
        checkbox_char = match.group(1)
        task_text = match.group(2)

        # Compute hash for this task
        task_hash = compute_task_hash(task_text)

        # Check if we have stored state for this task (overrides markdown)
        if task_hash in self.task_states:
            is_checked = self.task_states[task_hash]
        else:
            # Use the state from markdown
            is_checked = checkbox_char.lower() == 'x'

        # Add CSS class to list item
        li_class = li_element.get('class', '')
        li_element.set('class', f'{li_class} task-item'.strip())

        # Create checkbox element
        checkbox = etree.Element('input')
        checkbox.set('type', 'checkbox')
        checkbox.set('class', 'task-checkbox')
        checkbox.set('data-hash', task_hash)
        if is_checked:
            checkbox.set('checked', 'checked')

        # Clear the list item and rebuild with checkbox
        if len(li_element) > 0 and li_element[0].tag == 'p':
            # Remove the paragraph and replace with checkbox + text
            p_elem = li_element[0]
            li_element.remove(p_elem)

            # Insert checkbox
            li_element.insert(0, checkbox)

            # Add the task text
            if li_element.text:
                li_element.text = ' ' + task_text
            else:
                checkbox.tail = ' ' + task_text

            # Preserve any remaining content from the paragraph
            if p_elem.tail:
                if len(li_element) > 0:
                    last = li_element[-1]
                    last.tail = (last.tail or '') + p_elem.tail
                else:
                    checkbox.tail = (checkbox.tail or '') + p_elem.tail

            # Add back any children from the paragraph (like links, emphasis, etc.)
            for child in p_elem:
                li_element.append(child)
        else:
            # Direct text in list item
            li_element.text = ''
            li_element.insert(0, checkbox)
            checkbox.tail = ' ' + task_text


class TaskListExtension(Extension):
    """
    Markdown extension for interactive task lists.

    Usage:
        md = markdown.Markdown(extensions=[TaskListExtension(task_states={...})])

    Config:
        task_states: dict mapping task hashes to boolean checked state
    """

    def __init__(self, **kwargs):
        self.config = {
            'task_states': [{}, 'Dictionary of task states - Default: {}']
        }
        super().__init__(**kwargs)

    def extendMarkdown(self, md):
        """Register the tree processor."""
        task_states = self.getConfig('task_states')
        processor = TaskListTreeprocessor(md, task_states)
        md.treeprocessors.register(processor, 'tasklist', 15)


def makeExtension(**kwargs):
    """Return an instance of the extension."""
    return TaskListExtension(**kwargs)


def update_task_in_markdown(markdown_content, task_hash, checked):
    """
    Update a specific task's checkbox state in markdown content.

    Args:
        markdown_content: The markdown text containing task lists
        task_hash: The hash of the task to update
        checked: Boolean, True for [x], False for [ ]

    Returns:
        Updated markdown content with the task's checkbox state changed
    """
    if not markdown_content:
        return markdown_content

    lines = markdown_content.split('\n')
    task_pattern = re.compile(r'^(\s*[-*]\s+)\[([ xX])\]\s+(.*)$')

    for i, line in enumerate(lines):
        match = task_pattern.match(line)
        if match:
            prefix = match.group(1)  # "- " or "* " with any leading whitespace
            task_text = match.group(3)

            # Check if this is the task we're looking for
            if compute_task_hash(task_text) == task_hash:
                # Update the checkbox state
                new_checkbox = 'x' if checked else ' '
                lines[i] = f'{prefix}[{new_checkbox}] {task_text}'
                break

    return '\n'.join(lines)
