# #region AssistantDispatch [C:4] [TYPE Module] [SEMANTICS assistant, dispatch, confirm, execution, orchestration]
# @BRIEF Intent dispatch engine and backward-compat wrapper around the central tool registry.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
# @RELATION DEPENDS_ON -> [AssistantSchemas]
# @INVARIANT  Unsupported operations are rejected via HTTPException(400).
# @INVARIANT  All tool handlers live in _tool_registry — _dispatch is a thin wrapper.

from __future__ import annotations

from typing import Any

from fastapi import HTTPException
from sqlalchemy.orm import Session

from src.core.config_manager import ConfigManager
from src.core.logger import belief_scope, logger
from src.core.task_manager import TaskManager
from src.schemas.auth import User
from src.services.git_service import GitService

from ._history import _coerce_query_bool
from ._resolvers import (
    _get_environment_name_by_id,
    _resolve_dashboard_id_entity,
    _resolve_env_id,
)
from ._schemas import AssistantAction
from ._tool_registry import dispatch as _registry_dispatch

# #region _get_git_service [TYPE Function]
# @BRIEF Lazy-init GitService singleton to avoid crash at module import time when /app/storage/ is unavailable.
# @POST  Returns GitService instance (created once, cached).
# @SIDE_EFFECT  May attempt to create /app/storage/repositories on first call.
_git_service_instance: GitService | None = None


def _get_git_service() -> GitService:
    global _git_service_instance
    if _git_service_instance is None:
        _git_service_instance = GitService()
    return _git_service_instance


# #endregion _get_git_service


# #region _clarification_text_for_intent [C:2] [TYPE Function]
# @BRIEF Convert technical missing-parameter errors into user-facing clarification prompts.
# @PRE  state was classified as needs_clarification for current intent/error combination.
# @POST  Returned text is human-readable and actionable for target operation.
def _clarification_text_for_intent(
    intent: dict[str, Any] | None, detail_text: str
) -> str:
    operation = (intent or {}).get("operation")
    guidance_by_operation: dict[str, str] = {
        "run_llm_validation": (
            "Нужно уточнение для запуска LLM-валидации: Укажите дашборд (id или slug), окружение и провайдер LLM."
        ),
        "run_llm_documentation": (
            "Нужно уточнение для генерации документации: Укажите dataset_id, окружение и провайдер LLM."
        ),
        "create_branch": "Нужно уточнение: укажите дашборд (id/slug/title) и имя ветки.",
        "commit_changes": "Нужно уточнение: укажите дашборд (id/slug/title) для коммита.",
        "deploy_dashboard": "Нужно уточнение: укажите дашборд (id/slug/title) и целевое окружение.",
        "execute_migration": "Нужно уточнение: укажите дашборд (id/slug/title), source_env и target_env.",
        "run_backup": "Нужно уточнение: укажите окружение и при необходимости дашборд (id/slug/title).",
    }
    return guidance_by_operation.get(operation, detail_text)


# #endregion _clarification_text_for_intent


# #region _async_confirmation_summary [C:4] [TYPE Function]
# @BRIEF Build human-readable confirmation prompt for an intent before execution.
# @PRE  actions is a non-empty list of planned review actions.
# @POST  Returns a formatted summary string suitable for display to the user.
# @SIDE_EFFECT  None - pure formatting function.
async def _async_confirmation_summary(intent: dict[str, Any], config_manager: ConfigManager, db: Session) -> str:
    with belief_scope('_confirmation_summary'):
        logger.reason('Belief protocol reasoning checkpoint for _confirmation_summary')
        operation = intent.get('operation', '')
        entities = intent.get('entities', {})
        descriptions: dict[str, tuple[str, str]] = {
            'create_branch': (
                '📝 Создание ветки в Git',
                'Будет создана ветка **{branch}** для дашборда **{dashboard}**. '
                'Изменения можно будет коммитить в эту ветку.'
            ),
            'commit_changes': (
                '📝 Коммит изменений в Git',
                'Изменения дашборда **{dashboard}** будут закоммичены в Git-репозиторий. '
                'После коммита их можно будет развернуть на других окружениях.'
            ),
            'deploy_dashboard': (
                '🚀 Деплой дашборда',
                'Дашборд **{dashboard}** будет развёрнут в окружение **{env}**. '
                'Изменения станут доступны на целевом окружении.'
            ),
            'execute_migration': (
                '🔄 Миграция дашборда',
                'Дашборд **{dashboard}** будет перенесён с **{src}** на **{tgt}**.'
            ),
            'run_backup': (
                '💾 Бэкап',
                'Будет запущено резервное копирование окружения **{env}**{dashboard}.'
            ),
            'run_llm_validation': (
                '🤖 LLM-валидация дашборда',
                'Будет запущена LLM-валидация дашборда **{dashboard}**{env}. '
                'Это займёт некоторое время.'
            ),
            'run_llm_documentation': (
                '📄 Генерация документации',
                'Будет сгенерирована документация для датасета **{dataset}**{env}.'
            ),
            'start_maintenance': (
                '🛠 Обслуживание',
                'Будет запущено окно обслуживания для таблиц **{tables}**.'
            ),
            'end_maintenance': (
                '✅ Завершение обслуживания',
                'Обслуживание будет завершено{event}.'
            ),
        }
        entry = descriptions.get(operation)
        if not entry:
            logger.reflect('Belief protocol postcondition checkpoint for _confirmation_summary')
            return 'Подтвердите выполнение операции или отмените.'

        title, desc = entry

        def _label(value: Any, prefix: str=' ') -> str:
            logger.reflect('Belief protocol postcondition checkpoint for _confirmation_summary')
            return f'{prefix}{value}' if value else ''
        dashboard = entities.get('dashboard_id') or entities.get('dashboard_ref')
        env_val = entities.get('environment') or entities.get('target_env')
        dataset_val = entities.get('dataset_id')
        event_val = entities.get('event_id')
        tables_val = entities.get('tables')
        tables_str = ', '.join(tables_val[:5]) if isinstance(tables_val, list) else str(tables_val or '')
        text = desc.format(
            branch=_label(entities.get('branch_name')),
            dashboard=_label(dashboard),
            env=_label(env_val),
            src=_label(entities.get('source_env')),
            tgt=_label(entities.get('target_env')),
            dataset=_label(dataset_val),
            tables=tables_str if tables_str else '?',
            event=_label(event_val),
        )
        text = f'**{title}**\n\n{text}'
        if operation == 'execute_migration':
            flags = []
            flags.append('маппинг БД: ' + ('ВКЛ' if _coerce_query_bool(entities.get('replace_db_config', False)) else 'ВЫКЛ'))
            flags.append('исправление кроссфильтров: ' + ('ВКЛ' if _coerce_query_bool(entities.get('fix_cross_filters', True)) else 'ВЫКЛ'))
            dry_run_enabled = _coerce_query_bool(entities.get('dry_run', False))
            flags.append('отчет dry-run: ' + ('ВКЛ' if dry_run_enabled else 'ВЫКЛ'))
            text += f" ({', '.join(flags)})"
            if dry_run_enabled:
                try:
                    from src.core.migration.dry_run_orchestrator import MigrationDryRunService
                    from src.core.superset_client import SupersetClient
                    from src.models.dashboard import DashboardSelection
                    src_token = entities.get('source_env')
                    tgt_token = entities.get('target_env')
                    dashboard_id = _resolve_dashboard_id_entity(entities, config_manager, env_hint=src_token)
                    if dashboard_id and src_token and tgt_token:
                        src_env_id = _resolve_env_id(src_token, config_manager)
                        tgt_env_id = _resolve_env_id(tgt_token, config_manager)
                        if src_env_id and tgt_env_id:
                            env_map = {env.id: env for env in config_manager.get_environments()}
                            source_env = env_map.get(src_env_id)
                            target_env = env_map.get(tgt_env_id)
                            if source_env and target_env and (source_env.id != target_env.id):
                                selection = DashboardSelection(source_env_id=source_env.id, target_env_id=target_env.id, selected_ids=[dashboard_id], replace_db_config=_coerce_query_bool(entities.get('replace_db_config', False)), fix_cross_filters=_coerce_query_bool(entities.get('fix_cross_filters', True)))
                                service = MigrationDryRunService()
                                source_client = SupersetClient(source_env)
                                target_client = SupersetClient(target_env)
                                report = service.run(selection, source_client, target_client, db)
                                s = report.get('summary', {})
                                dash_s = s.get('dashboards', {})
                                charts_s = s.get('charts', {})
                                ds_s = s.get('datasets', {})
                                creates = dash_s.get('create', 0) + charts_s.get('create', 0) + ds_s.get('create', 0)
                                updates = dash_s.get('update', 0) + charts_s.get('update', 0) + ds_s.get('update', 0)
                                deletes = dash_s.get('delete', 0) + charts_s.get('delete', 0) + ds_s.get('delete', 0)
                                text += f'\n\nОтчет dry-run:\n- Будет создано новых объектов: {creates}\n- Будет обновлено: {updates}\n- Будет удалено: {deletes}'
                            else:
                                text += '\n\n(Не удалось загрузить отчет dry-run: неверные окружения).'
                except Exception as e:
                    import traceback
                    logger.warning('[assistant.dry_run_summary][failed] Exception: %s\n%s', e, traceback.format_exc())
                    text += f'\n\n(Не удалось загрузить отчет dry-run: {e}).'
        logger.reflect('Belief protocol postcondition checkpoint for _confirmation_summary')
        return f'Выполнить: {text}. Подтвердите или отмените.'


# #endregion _async_confirmation_summary


# #region _dispatch_intent [C:1] [TYPE Function]
# @BRIEF Backward-compat wrapper around the central tool registry dispatch.
# @DEPRECATED Use `dispatch()` from `_tool_registry` directly.
async def _dispatch_intent(intent: dict[str, Any], current_user: User, task_manager: TaskManager, config_manager: ConfigManager, db: Session) -> tuple[str, str | None, list[AssistantAction]]:
    """Backward-compat wrapper around the central tool registry dispatch."""
    operation = intent.get('operation')
    return await _registry_dispatch(operation, intent, current_user, task_manager, config_manager, db)


# #endregion _dispatch_intent


# #endregion AssistantDispatch
