# #region SearchPluginModule [TYPE Module] [SEMANTICS search, dataset, text, pattern, superset]
# @BRIEF Implements a plugin for searching text patterns across all datasets in a specific Superset environment.
# @LAYER Plugin
# @BRIEF Plugin for text search across datasets. Inherits PluginBase.
# @RELATION DEPENDS_ON -> [TaskContext]

import re
from typing import Any

from ..core.logger import belief_scope, logger
from ..core.plugin_base import PluginBase
from ..core.superset_client import SupersetClient
from ..core.task_manager.context import TaskContext


# #region SearchPlugin [TYPE Class]
# @BRIEF Plugin for searching text patterns in Superset datasets.
class SearchPlugin(PluginBase):
    """
    Plugin for searching text patterns in Superset datasets.
    """

    @property
    # region id [TYPE Function]
    # @PURPOSE: Returns the unique identifier for the search plugin.
    # @PRE  Plugin instance exists.
    # @POST  Returns string ID.
    # @RETURN  str - "search-datasets"
    def id(self) -> str:
        with belief_scope("id"):
            return "search-datasets"
    # endregion id

    @property
    # region name [TYPE Function]
    # @PURPOSE: Returns the human-readable name of the search plugin.
    # @PRE  Plugin instance exists.
    # @POST  Returns string name.
    # @RETURN  str - Plugin name.
    def name(self) -> str:
        with belief_scope("name"):
            return "Search Datasets"
    # endregion name

    @property
    # region description [TYPE Function]
    # @PURPOSE: Returns a description of the search plugin.
    # @PRE  Plugin instance exists.
    # @POST  Returns string description.
    # @RETURN  str - Plugin description.
    def description(self) -> str:
        with belief_scope("description"):
            return "Search for text patterns across all datasets in a specific environment."
    # endregion description

    @property
    # region version [TYPE Function]
    # @PURPOSE: Returns the version of the search plugin.
    # @PRE  Plugin instance exists.
    # @POST  Returns string version.
    # @RETURN  str - "1.0.0"
    def version(self) -> str:
        with belief_scope("version"):
            return "1.0.0"
    # endregion version

    @property
    # region ui_route [TYPE Function]
    # @PURPOSE: Returns the frontend route for the search plugin.
    # @RETURN  str - "/tools/search"
    def ui_route(self) -> str:
        with belief_scope("ui_route"):
            return "/tools/search"
    # endregion ui_route

    # region get_schema [TYPE Function]
    # @PURPOSE: Returns the JSON schema for the search plugin parameters.
    # @PRE  Plugin instance exists.
    # @POST  Returns dictionary schema.
    # @RETURN  Dict[str, Any] - JSON schema.
    def get_schema(self) -> dict[str, Any]:
        with belief_scope("get_schema"):
            return {
            "type": "object",
            "properties": {
                "env": {
                    "type": "string",
                    "title": "Environment",
                    "description": "The Superset environment to search in (e.g., 'dev', 'prod')."
                },
                "query": {
                    "type": "string",
                    "title": "Search Query (Regex)",
                    "description": "The regex pattern to search for."
                }
            },
            "required": ["env", "query"]
        }
    # endregion get_schema

    # region execute [TYPE Function]
    # @PURPOSE: Executes the dataset search logic with TaskContext support.
    # @PARAM  params (Dict[str, Any]) - Search parameters.
    # @PARAM  context (Optional[TaskContext]) - Task context for logging with source attribution.
    # @PRE  Params contain valid 'env' and 'query'.
    # @POST  Returns a dictionary with count and results list.
    # @RETURN  Dict[str, Any] - Search results.
    async def execute(self, params: dict[str, Any], context: TaskContext | None = None) -> dict[str, Any]:
        with belief_scope("SearchPlugin.execute", f"params={params}"):
            env_name = params.get("env")
            search_query = params.get("query")

            # Use TaskContext logger if available, otherwise fall back to app logger
            log = context.logger if context else logger

            # Create sub-loggers for different components
            log.with_source("superset_api") if context else log
            search_log = log.with_source("search") if context else log

            if not env_name or not search_query:
                log.error("Missing required parameters: env, query")
                raise ValueError("Missing required parameters: env, query")

            # Get config and initialize client
            from ..dependencies import get_config_manager
            config_manager = get_config_manager()
            env_config = config_manager.get_environment(env_name)
            if not env_config:
                log.error(f"Environment '{env_name}' not found in configuration.")
                raise ValueError(f"Environment '{env_name}' not found in configuration.")

            client = SupersetClient(env_config)
            client.authenticate()

            log.info(f"Searching for pattern: '{search_query}' in environment: {env_name}")

            try:
                # Ported logic from search_script.py
                _, datasets = client.get_datasets(query={"columns": ["id", "table_name", "sql", "database", "columns"]})

                if not datasets:
                    search_log.warning("No datasets found.")
                    return {"count": 0, "results": []}

                pattern = re.compile(search_query, re.IGNORECASE)
                results = []

                for dataset in datasets:
                    dataset_id = dataset.get('id')
                    dataset_name = dataset.get('table_name', 'Unknown')
                    if not dataset_id:
                        continue

                    for field, value in dataset.items():
                        value_str = str(value)
                        if pattern.search(value_str):
                            match_obj = pattern.search(value_str)
                            results.append({
                                "dataset_id": dataset_id,
                                "dataset_name": dataset_name,
                                "field": field,
                                "match_context": self._get_context(value_str, match_obj.group() if match_obj else ""),
                                "full_value": value_str
                            })

                search_log.info(f"Found matches in {len(results)} locations.")
                return {
                    "count": len(results),
                    "results": results
                }

            except re.error as e:
                search_log.error(f"Invalid regex pattern: {e}")
                raise ValueError(f"Invalid regex pattern: {e}")
            except Exception as e:
                log.error(f"Error during search: {e}")
                raise
    # endregion execute

    # region _get_context [TYPE Function]
    # @PURPOSE: Extracts a small context around the match for display.
    # @PARAM  text (str) - The full text to extract context from.
    # @PARAM  match_text (str) - The matched text pattern.
    # @PARAM  context_lines (int) - Number of lines of context to include.
    # @PRE  text and match_text must be strings.
    # @POST  Returns context string.
    # @RETURN  str - Extracted context.
    def _get_context(self, text: str, match_text: str, context_lines: int = 1) -> str:
        """
        Extracts a small context around the match for display.
        """
        with belief_scope("_get_context"):
            if not match_text:
                return text[:100] + "..." if len(text) > 100 else text

            lines = text.splitlines()
        match_line_index = -1
        for i, line in enumerate(lines):
            if match_text in line:
                match_line_index = i
                break

            if match_line_index != -1:
                start = max(0, match_line_index - context_lines)
                end = min(len(lines), match_line_index + context_lines + 1)
                context = []
                for i in range(start, end):
                    line_content = lines[i]
                    if i == match_line_index:
                        context.append(f"==> {line_content}")
                    else:
                        context.append(f"    {line_content}")
                return "\n".join(context)

            return text[:100] + "..." if len(text) > 100 else text
    # endregion _get_context

# #endregion SearchPlugin
# #endregion SearchPluginModule
