"""LLM-powered tag suggestions for notes and tasks."""

import json
import logging
import re

from fabledassistant.config import Config
from fabledassistant.services.llm import generate_completion
from fabledassistant.services.notes import get_all_tags
from fabledassistant.services.settings import get_setting

logger = logging.getLogger(__name__)


async def suggest_tags(user_id: int, title: str, body: str, current_tags: list[str] | None = None) -> list[str]:
    """Suggest relevant tags for a note/task based on its content.

    Returns a list of tag strings (without # prefix), excluding tags
    already present in current_tags.
    """
    if not title.strip() and not body.strip():
        return []

    existing_tags = await get_all_tags(user_id)
    model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL)

    existing_list = ", ".join(f"#{t}" for t in existing_tags) if existing_tags else "(none yet)"

    messages = [
        {
            "role": "system",
            "content": (
                "You are a tag suggestion assistant. Given a note's title and body, "
                "suggest 3-5 relevant tags. Prefer reusing existing tags when they fit, "
                "but you may suggest new ones too. "
                "Tags must use hyphens for multi-word names (e.g. science-fiction, space-travel) — never spaces. "
                "Reply with ONLY a JSON array of tag strings (without # prefix). "
                'Example: ["meeting", "project/alpha", "science-fiction"]\n\n'
                f"Existing tags: {existing_list}"
            ),
        },
        {
            "role": "user",
            "content": f"Title: {title}\n\nBody:\n{body[:2000]}",
        },
    ]

    try:
        response = await generate_completion(messages, model)
    except Exception:
        logger.warning("Tag suggestion LLM call failed", exc_info=True)
        return []

    tags = _parse_tag_list(response)

    # Filter out tags already applied
    existing = set(current_tags or [])
    tags = [t for t in tags if t not in existing]

    return tags[:5]


def _parse_tag_list(response: str) -> list[str]:
    """Parse a JSON array of tags from LLM response, with fallback for markdown wrapping."""
    text = response.strip()

    # Try direct JSON parse
    def _clean(tag: str) -> str:
        return str(tag).strip().lstrip("#").replace(" ", "-")

    try:
        parsed = json.loads(text)
        if isinstance(parsed, list):
            return [_clean(t) for t in parsed if t]
    except json.JSONDecodeError:
        pass

    # Fallback: extract JSON array from markdown code block
    match = re.search(r"\[.*?\]", text, re.DOTALL)
    if match:
        try:
            parsed = json.loads(match.group())
            if isinstance(parsed, list):
                return [_clean(t) for t in parsed if t]
        except json.JSONDecodeError:
            pass

    logger.warning("Could not parse tag suggestions from LLM response: %s", text[:200])
    return []
