#!/usr/bin/env python3
"""
CLI-инструмент для тестирования Voice Analytics Hub Backend.

Использование:
    # Интерактивный режим
    python tools/test_chat.py

    # Один запрос
    python tools/test_chat.py --text "Покажи выручку по брендам"

    # Сценарий
    python tools/test_chat.py --scenario revenue_by_brand

    # С сохранением сессии
    python tools/test_chat.py --session my-test --thread thread-1
"""

import asyncio
import argparse
import json
import os
import sys
import uuid
from datetime import datetime
from pathlib import Path
from typing import Optional

import httpx
import yaml
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
from rich.table import Table
from rich import box

# Добавляем путь к корню проекта
sys.path.insert(0, str(Path(__file__).parent.parent))

console = Console()

try:
    from dotenv import load_dotenv
except Exception:  # pragma: no cover - optional dependency fallback
    load_dotenv = None

if load_dotenv:
    load_dotenv()


def _default_base_url() -> str:
    api_base = os.getenv("API_BASE_URL")
    if api_base:
        return api_base
    host = os.getenv("API_HOST", "localhost")
    if host in ("0.0.0.0", "::"):
        host = "localhost"
    port = os.getenv("API_PORT", "8000")
    return f"http://{host}:{port}"


class ChatTester:
    """CLI-инструмент для тестирования чата."""

    def __init__(
        self,
        base_url: str = _default_base_url(),
        session_id: Optional[str] = None,
        thread_id: Optional[str] = None,
        log_file: Optional[Path] = None
    ):
        self.base_url = base_url
        self.session_id = session_id or str(uuid.uuid4())
        self.thread_id = thread_id or str(uuid.uuid4())
        self.log_file = log_file
        self.turn_counter = 0

        console.print(f"[bold green]Session ID:[/bold green] {self.session_id}")
        console.print(f"[bold green]Thread ID:[/bold green] {self.thread_id}")
        console.print()

    def _log(self, message: str, data: Optional[dict] = None):
        """Логирование в файл."""
        if self.log_file:
            with open(self.log_file, "a", encoding="utf-8") as f:
                timestamp = datetime.utcnow().isoformat()
                f.write(f"\n{'='*80}\n")
                f.write(f"[{timestamp}] {message}\n")
                if data:
                    f.write(json.dumps(data, indent=2, ensure_ascii=False))
                    f.write("\n")

    async def send_message(self, text: str, mode: str = "chat") -> dict:
        """Отправить сообщение в бэкенд."""
        self.turn_counter += 1

        # Формируем запрос
        request = {
            "session_id": self.session_id,
            "thread_id": self.thread_id,
            "mode": mode,
            "input": {
                "type": "text",
                "text": text,
                "audio_b64": None,
                "audio_format": None,
                "sample_rate_hz": 16000,
                "language": "ru-RU"
            },
            "client": {
                "device": "iphone",
                "timezone": "Europe/Prague",
                "voice_enabled": False,
                "tts_enabled": False,
                "speech_provider_preference": "azure"
            }
        }

        # Показываем запрос
        console.print(Panel(
            f"[bold cyan]Turn #{self.turn_counter}[/bold cyan]\n"
            f"[yellow]Вы:[/yellow] {text}",
            box=box.ROUNDED
        ))

        self._log(f"USER INPUT (Turn #{self.turn_counter})", {"text": text})
        self._log("REQUEST TO BACKEND", request)

        # Отправляем запрос
        async with httpx.AsyncClient(timeout=120.0) as client:
            try:
                response = await client.post(
                    f"{self.base_url}/v1/chat/turn",
                    json=request
                )
                response.raise_for_status()
                result = response.json()

                self._log("RESPONSE FROM BACKEND", result)
                return result

            except httpx.HTTPError as e:
                console.print(f"[bold red]HTTP Error:[/bold red] {str(e)}")
                self._log("HTTP ERROR", {"error": str(e)})
                if hasattr(e, "response") and e.response is not None:
                    try:
                        error_detail = e.response.json()
                        console.print(Panel(
                            Syntax(json.dumps(error_detail, indent=2), "json"),
                            title="Error Details",
                            border_style="red"
                        ))
                        self._log("ERROR DETAILS", error_detail)
                    except:
                        pass
                raise

    def display_response(self, response: dict):
        """Отобразить ответ от бэкенда."""

        # Текст ассистента
        assistant_text = response.get("assistant_text", "")
        console.print(Panel(
            f"[bold green]Ассистент:[/bold green] {assistant_text}",
            box=box.ROUNDED
        ))

        # Проверка на уточнение
        clarification = response.get("clarification", {})
        if clarification.get("needed"):
            console.print("\n[bold yellow]⚠️  Требуется уточнение[/bold yellow]")
            console.print(f"Вопрос: {clarification.get('question')}")

            if clarification.get("options"):
                console.print("\nВарианты:")
                for i, option in enumerate(clarification.get("options", []), 1):
                    console.print(f"  {i}. {option}")
            console.print()
            return

        # UI данные
        ui = response.get("ui", {})
        active_widget = ui.get("active_widget", {})

        # Таблица с информацией о виджете
        table = Table(title="UI Information", box=box.SIMPLE)
        table.add_column("Field", style="cyan")
        table.add_column("Value", style="white")

        table.add_row("Widget ID", active_widget.get("widget_id", "N/A"))
        table.add_row("Dataset ID", active_widget.get("dataset_id", "N/A"))
        table.add_row("Dataset Changed", str(ui.get("dataset_changed", False)))
        table.add_row("Cards Count", str(len(ui.get("cards", []))))

        console.print(table)

        # Payload
        payload = active_widget.get("payload", {})
        if payload:
            console.print("\n[bold]Widget Payload:[/bold]")

            # Time range
            time_range = payload.get("time", {})
            if time_range:
                console.print(f"  Time: {time_range.get('from')} → {time_range.get('to')} ({time_range.get('granularity')})")

            # Dimensions
            dimensions = payload.get("dimensions", [])
            if dimensions:
                console.print(f"  Dimensions: {', '.join(dimensions)}")

            # Metric
            metric = payload.get("metric")
            if metric:
                console.print(f"  Metric: {metric}")

            # Filters
            filters = payload.get("filters", [])
            if filters:
                console.print(f"  Filters: {filters}")

            # Query result
            query_result = payload.get("query_result")
            if query_result:
                console.print("\n[bold]Query Result:[/bold]")
                columns = query_result.get("columns", [])
                rows = query_result.get("rows", [])

                # Создаем таблицу с результатами
                result_table = Table(box=box.SIMPLE_HEAD)
                for col in columns:
                    result_table.add_column(col, style="cyan")

                for row in rows[:10]:  # Показываем первые 10 строк
                    result_table.add_row(*[str(row.get(col, "")) for col in columns])

                console.print(result_table)

                if len(rows) > 10:
                    console.print(f"  ... и еще {len(rows) - 10} строк")

        console.print()

    async def interactive_mode(self):
        """Интерактивный режим."""
        console.print("[bold]Интерактивный режим. Введите 'exit' для выхода.[/bold]\n")

        while True:
            try:
                user_input = console.input("[bold yellow]Вы:[/bold yellow] ")

                if user_input.strip().lower() in ["exit", "quit", "выход"]:
                    console.print("[bold]До свидания![/bold]")
                    break

                if not user_input.strip():
                    continue

                response = await self.send_message(user_input)
                self.display_response(response)

            except KeyboardInterrupt:
                console.print("\n[bold]До свидания![/bold]")
                break
            except Exception as e:
                console.print(f"[bold red]Error:[/bold red] {str(e)}")
                import traceback
                traceback.print_exc()

    async def run_scenario(self, scenario_file: Path, scenario_name: str):
        """Запустить тестовый сценарий."""

        # Загружаем сценарии
        with open(scenario_file, "r", encoding="utf-8") as f:
            scenarios = yaml.safe_load(f)

        # Находим нужный сценарий
        scenario = None
        for s in scenarios.get("scenarios", []):
            if s.get("name") == scenario_name:
                scenario = s
                break

        if not scenario:
            console.print(f"[bold red]Scenario '{scenario_name}' not found![/bold red]")
            return

        console.print(Panel(
            f"[bold]Running scenario:[/bold] {scenario.get('name')}\n"
            f"[dim]{scenario.get('description', '')}[/dim]",
            box=box.DOUBLE
        ))

        # Выполняем диалог
        for turn in scenario.get("turns", []):
            response = await self.send_message(turn)
            self.display_response(response)

            # Пауза между запросами
            await asyncio.sleep(1)


async def main():
    parser = argparse.ArgumentParser(description="CLI для тестирования Voice Analytics Hub Backend")

    parser.add_argument(
        "--url",
        default=_default_base_url(),
        help="URL бэкенда (default: from .env or http://$API_HOST:$API_PORT)"
    )
    parser.add_argument(
        "--session",
        help="Session ID (создается автоматически если не указан)"
    )
    parser.add_argument(
        "--thread",
        help="Thread ID (создается автоматически если не указан)"
    )
    parser.add_argument(
        "--text",
        help="Отправить один запрос и выйти"
    )
    parser.add_argument(
        "--scenario",
        help="Запустить тестовый сценарий по имени"
    )
    parser.add_argument(
        "--scenarios-file",
        type=Path,
        default=Path(__file__).parent / "test_scenarios.yaml",
        help="Путь к файлу со сценариями"
    )
    parser.add_argument(
        "--log",
        type=Path,
        help="Путь к файлу логов"
    )

    args = parser.parse_args()

    # Создаем тестер
    tester = ChatTester(
        base_url=args.url,
        session_id=args.session,
        thread_id=args.thread,
        log_file=args.log
    )

    # Выбираем режим
    if args.text:
        # Один запрос
        response = await tester.send_message(args.text)
        tester.display_response(response)

    elif args.scenario:
        # Сценарий
        await tester.run_scenario(args.scenarios_file, args.scenario)

    else:
        # Интерактивный режим
        await tester.interactive_mode()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        console.print("\n[bold]Interrupted[/bold]")
