"""
Tag management for StarPunk

Functions:
    normalize_tag: Normalize tag string for storage
    get_or_create_tag: Get existing tag or create new one
    add_tags_to_note: Associate tags with a note
    remove_tags_from_note: Remove tag associations
    get_note_tags: Get all tags for a note
    get_notes_by_tag: Get all notes with a specific tag
"""

import re
from typing import Optional

from flask import current_app

from starpunk.database import get_db


def normalize_tag(tag: str) -> tuple[str, str]:
    """
    Normalize a tag string

    Args:
        tag: Raw tag string

    Returns:
        Tuple of (normalized_name, display_name)

    Examples:
        >>> normalize_tag("  IndieWeb  ")
        ('indieweb', 'IndieWeb')
        >>> normalize_tag("Machine Learning")
        ('machine-learning', 'Machine Learning')
    """
    # Step 1: Strip whitespace for display_name
    display_name = tag.strip()

    # Step 2: Strip leading # characters
    normalized = display_name.lstrip('#')

    # Step 3: Replace spaces and slashes with hyphens
    normalized = normalized.replace(' ', '-').replace('/', '-')

    # Step 4: Remove characters not in [a-zA-Z0-9_-]
    normalized = re.sub(r'[^a-zA-Z0-9_-]', '', normalized)

    # Step 5: Collapse consecutive hyphens to single hyphen
    normalized = re.sub(r'-+', '-', normalized)

    # Step 6: Strip leading/trailing hyphens
    normalized = normalized.strip('-')

    # Step 7: Convert to lowercase for normalized name
    normalized = normalized.lower()

    return normalized, display_name


def get_or_create_tag(display_name: str) -> int:
    """
    Get existing tag ID or create new tag

    Args:
        display_name: Tag display name

    Returns:
        Tag ID
    """
    db = get_db(current_app)
    normalized_name, clean_display = normalize_tag(display_name)

    # Try to find existing tag
    tag = db.execute(
        "SELECT id FROM tags WHERE name = ?",
        (normalized_name,)
    ).fetchone()

    if tag:
        return tag['id']

    # Create new tag
    cursor = db.execute(
        "INSERT INTO tags (name, display_name) VALUES (?, ?)",
        (normalized_name, clean_display)
    )
    db.commit()
    return cursor.lastrowid


def add_tags_to_note(note_id: int, tags: list[str]) -> None:
    """
    Associate tags with a note

    Replaces all existing tags for the note.

    Args:
        note_id: Note database ID
        tags: List of tag display names
    """
    db = get_db(current_app)

    # Remove existing tags
    db.execute("DELETE FROM note_tags WHERE note_id = ?", (note_id,))

    # Add new tags
    for tag_display in tags:
        tag_id = get_or_create_tag(tag_display)
        db.execute(
            "INSERT INTO note_tags (note_id, tag_id) VALUES (?, ?)",
            (note_id, tag_id)
        )

    db.commit()


def get_note_tags(note_id: int) -> list[dict]:
    """
    Get all tags for a note

    Args:
        note_id: Note database ID

    Returns:
        List of tag dicts with 'name' and 'display_name'
        Ordered alphabetically by display_name (case-insensitive)

    Query should use: ORDER BY LOWER(tags.display_name) ASC
    """
    db = get_db(current_app)
    tags = db.execute(
        """
        SELECT tags.name, tags.display_name
        FROM tags
        JOIN note_tags ON tags.id = note_tags.tag_id
        WHERE note_tags.note_id = ?
        ORDER BY LOWER(tags.display_name) ASC
        """,
        (note_id,)
    ).fetchall()

    return [dict(tag) for tag in tags]


def get_tag_by_name(name: str) -> Optional[dict]:
    """
    Get tag by normalized name

    Args:
        name: Tag name (will be normalized before lookup)

    Returns:
        Tag dict with 'id', 'name', 'display_name' or None
    """
    db = get_db(current_app)
    normalized_name, _ = normalize_tag(name)

    tag = db.execute(
        "SELECT id, name, display_name FROM tags WHERE name = ?",
        (normalized_name,)
    ).fetchone()

    return dict(tag) if tag else None


def get_notes_by_tag(tag_name: str) -> list:
    """
    Get all published notes with a specific tag

    Args:
        tag_name: Normalized tag name

    Returns:
        List of Note objects with tags pre-loaded
    """
    from starpunk.notes import get_note

    db = get_db(current_app)
    rows = db.execute(
        """
        SELECT DISTINCT notes.id
        FROM notes
        JOIN note_tags ON notes.id = note_tags.note_id
        JOIN tags ON note_tags.tag_id = tags.id
        WHERE tags.name = ? AND notes.published = 1
        ORDER BY notes.created_at DESC
        """,
        (tag_name,)
    ).fetchall()

    notes = []
    for row in rows:
        note = get_note(id=row['id'])
        if note:
            # Pre-load tags
            tags = get_note_tags(note.id)
            object.__setattr__(note, '_cached_tags', tags)
            notes.append(note)

    return notes


def parse_tag_input(input_string: str) -> list[str]:
    """
    Parse comma-separated tag input from admin form

    Splits on commas, trims whitespace, filters empties,
    deduplicates by normalized name (keeps first occurrence).

    Args:
        input_string: Comma-separated tags (e.g., "Python, IndieWeb, Web")

    Returns:
        List of unique tag display names

    Examples:
        >>> parse_tag_input("Python, IndieWeb, Web")
        ['Python', 'IndieWeb', 'Web']
        >>> parse_tag_input("Python, python, PYTHON")
        ['Python']  # First occurrence wins
        >>> parse_tag_input("  ,  , valid ,  ")
        ['valid']
    """
    if not input_string:
        return []

    # Split on commas and strip whitespace
    tags = [tag.strip() for tag in input_string.split(',')]

    # Filter empty strings
    tags = [tag for tag in tags if tag]

    # Deduplicate by normalized name (keep first occurrence)
    seen = set()
    unique_tags = []
    for tag in tags:
        normalized, _ = normalize_tag(tag)
        if normalized and normalized not in seen:
            seen.add(normalized)
            unique_tags.append(tag)

    return unique_tags
