"""Smart search engine for Cerebro tasks, messages, and attachments."""

from cerebro_mcp.connection import execute
from cerebro_mcp.converters import (
    task_to_dict, message_to_dict, attachment_to_dict,
    is_video_attachment, html_to_text,
)


async def get_all_tasks_recursive(parent_id: int, max_depth: int = 10) -> list[dict]:
    """Recursively get all tasks under parent_id."""
    if max_depth <= 0:
        return []

    children = await execute("task_children", parent_id)
    if not children:
        return []

    result = []
    for t in children:
        d = task_to_dict(t)
        result.append(d)
        # Check TASK_FLAG_HAS_CHILD (flag bit 32)
        flags = t[17] or 0
        if flags & (1 << 32):
            sub = await get_all_tasks_recursive(int(d["id"]), max_depth - 1)
            result.extend(sub)

    return result


async def filter_tasks(
    parent_id: int,
    status_ids: list[int] | None = None,
    activity_ids: list[int] | None = None,
    allocated_user_id: int | None = None,
    name_contains: str | None = None,
    recursive: bool = True,
    max_depth: int = 10,
) -> list[dict]:
    """Filter tasks by multiple criteria."""
    if recursive:
        tasks = await get_all_tasks_recursive(parent_id, max_depth)
    else:
        children = await execute("task_children", parent_id)
        tasks = [task_to_dict(t) for t in (children or [])]

    name_lower = name_contains.lower() if name_contains else None
    # Convert status/activity IDs to str sets for comparison with str dict values
    status_strs = {str(s) for s in status_ids} if status_ids else None
    activity_strs = {str(a) for a in activity_ids} if activity_ids else None

    filtered = []
    for t in tasks:
        if status_strs and t["status_id"] not in status_strs:
            continue
        if activity_strs and t["activity_id"] not in activity_strs:
            continue
        if allocated_user_id:
            alloc = t.get("allocated") or ""
            alloc_ids = [int(x) for x in alloc.split(";") if x.strip()]
            if allocated_user_id not in alloc_ids:
                continue
        if name_lower and name_lower not in (t["name"] or "").lower():
            continue
        filtered.append(t)

    return filtered


def _group_attachments(raw: list) -> dict[str, list[dict]]:
    """Group raw attachments by group_id, filtering deleted."""
    groups: dict[str, list[dict]] = {}
    for a in raw:
        d = attachment_to_dict(a)
        if d["deleted"]:
            continue
        groups.setdefault(d["group_id"], []).append(d)
    return groups


async def search_messages(
    task_ids: list[int],
    text_contains: str | None = None,
    message_types: list[int] | None = None,
    creator_name: str | None = None,
    has_attachments: bool | None = None,
    attachment_is_video: bool | None = None,
    limit: int = 50,
) -> list[dict]:
    """Search messages across multiple tasks."""
    if not task_ids:
        return []

    results = []
    text_lower = text_contains.lower() if text_contains else None
    creator_lower = creator_name.lower() if creator_name else None

    # Process in batches to avoid overwhelming the API
    batch_size = 50
    for i in range(0, len(task_ids), batch_size):
        batch = task_ids[i:i + batch_size]

        for tid in batch:
            messages = await execute("task_messages", tid)
            if not messages:
                continue

            for m in messages:
                msg = message_to_dict(m)

                if message_types and msg["type"] not in message_types:
                    continue
                if text_lower and text_lower not in (msg["text"] or "").lower():
                    continue
                if creator_lower and creator_lower not in (msg["creator_name"] or "").lower():
                    continue

                results.append(msg)

                if len(results) >= limit and not has_attachments and attachment_is_video is None:
                    return results

    # If we need attachment filtering, do it in a second pass
    if has_attachments is not None or attachment_is_video is not None:
        msg_ids = [int(m["id"]) for m in results]
        if msg_ids:
            raw_atts = await execute("message_attachments", set(msg_ids))
            att_by_msg: dict[str, list] = {}
            for a in (raw_atts or []):
                d = attachment_to_dict(a)
                if not d["deleted"]:
                    att_by_msg.setdefault(d["message_id"], []).append(d)

            filtered = []
            for msg in results:
                msg_atts = att_by_msg.get(msg["id"], [])
                has_att = len(msg_atts) > 0

                if has_attachments is True and not has_att:
                    continue
                if has_attachments is False and has_att:
                    continue

                if attachment_is_video is not None:
                    groups: dict[str, list[dict]] = {}
                    for a in msg_atts:
                        groups.setdefault(a["group_id"], []).append(a)
                    has_video = any(is_video_attachment(g) for g in groups.values())
                    if attachment_is_video and not has_video:
                        continue
                    if not attachment_is_video and has_video:
                        continue

                msg["attachments"] = msg_atts
                filtered.append(msg)
                if len(filtered) >= limit:
                    break
            return filtered

    return results[:limit]


async def search_attachments(
    task_ids: list[int],
    filename_contains: str | None = None,
    is_video: bool | None = None,
    min_size: int | None = None,
    limit: int = 50,
) -> list[dict]:
    """Search attachments across tasks."""
    if not task_ids:
        return []

    raw = await execute("task_attachments", set(task_ids))
    groups = _group_attachments(raw or [])

    fn_lower = filename_contains.lower() if filename_contains else None
    results = []

    for gid, atts in groups.items():
        # Get the main file entry (tag=0 file or tag=5 link)
        main = next((a for a in atts if a["tag"] in (0, 5)), None)
        if not main:
            continue

        if fn_lower and fn_lower not in (main["file_name"] or "").lower():
            continue
        if min_size and (main["file_size"] or 0) < min_size:
            continue
        if is_video is not None:
            vid = is_video_attachment(atts)
            if is_video and not vid:
                continue
            if not is_video and vid:
                continue

        main["is_video"] = is_video_attachment(atts)
        main["thumbnails"] = [a for a in atts if a["tag"] in (1, 2, 3)]
        results.append(main)

        if len(results) >= limit:
            break

    return results
