from typing import Tuple, Optional, Dict, List
from .base import BaseAIProductService
import logging

from django.db import models

logger = logging.getLogger(__name__)


class BouquetNameGenerator(BaseAIProductService):
    """
    Сервис для генерации и управления названиями букетов
    """

    DEFAULT_SYSTEM_PROMPT = (
        "Вы эксперт в создании красивых, привлекательных и продаваемых названий для букетов цветов. "
        "Ваша цель — генерировать запоминающиеся и выразительные названия, которые привлекут покупателей. "
        "Названия должны быть краткими (2-4 слов), креативными и соответствующими характеристикам букета. "
        "Избегайте общих терминов. Фокусируйтесь на эмоциях, эстетике"
    )

    # Константы
    MAX_TOKENS_GENERATION = 3000
    DEFAULT_COUNT = 500
    MAX_GENERATION_COUNT = 1000
    SKIP_PREFIXES = {'here', 'names', "i'm", 'sorry', 'i hope', 'hope'}

    def generate(
        self,
        count: int = 500,
        characteristics: Optional[str] = None,
        occasion: Optional[str] = None,
        language: str = "russian"
    ) -> Tuple[bool, str, Optional[Dict]]:
        """
        Генерация названий букетов

        Args:
            count: Количество названий для генерации
            characteristics: Характеристики букетов (например, "розы, лилии, яркий")
            occasion: П'occasion (например, "день рождения, Valentine's Day")
            language: Язык генерации

        Returns:
            Tuple: (success, message, data) где data содержит список названий
        """
        # Валидация параметров
        if count > self.MAX_GENERATION_COUNT:
            count = self.MAX_GENERATION_COUNT
            logger.warning(f"Count reduced to {self.MAX_GENERATION_COUNT}")

        logger.info(f"Генерация {count} названий для букетов")

        # Получаем доступный AI-сервис
        service = self.get_glm_service() or self.get_openrouter_service()
        if not service:
            return False, "Нет активных AI-интеграций", None

        # Формируем промпт
        prompt = f"Сгенерируй {count} креативных и привлекательных названий для букетов цветов"

        if characteristics:
            prompt += f" с следующими характеристиками: {characteristics}"

        if occasion:
            prompt += f" для праздника: {occasion}"

        prompt += (
            "\n\nТребования к каждому названию:\n"
            "- 2, 3 или 4 слова в равных пропорциях\n"
            "- Выразительные и эмоциональные\n"
            "- Продаваемые и запоминающиеся\n"
            "- Избегайте общих названий типа 'Букет #1'\n"
            "- Фокусируйтесь на красоте, романтике и подарках\n"
            "- Используйте прилагательные и описательные слова\n"
            "- Не используйте символы пунктуации в середине названий\n"
            "\nВерните названия в виде нумерованного списка, по одному на строку.\n"
            "Примеры хороших названий:\n"
            "- 2 слова: 'Весенние Розы', 'Летнее Сияние', 'Нежность', 'Романтика'\n"
            "- 3 слова: 'Весенний Вальс', 'Нежность Роз', 'Сияние Любви', 'Танец Цветов'\n"
            "- 4 слова: 'Шепот Весенней Нежности', 'Сияние Розовой Любви', 'Танец Цветов Весны', 'Шёпот Сердечной Романтики'"
        )

        # Вызов AI-сервиса
        success, msg, response = service.generate_text(
            prompt=prompt,
            system_prompt=self.DEFAULT_SYSTEM_PROMPT,
            max_tokens=3000  # Увеличиваем лимит для большего числа названий
        )

        if not success:
            return False, msg, None

        # Парсим результат
        names = self._parse_response(response.get('generated_text', ''))

        return True, f"Сгенерировано {len(names)} названий для букетов", {
            'names': names,
            'model': response.get('model'),
            'usage': response.get('usage')
        }

    def _parse_response(self, text: str) -> List[str]:
        """
        Парсит текстовый ответ AI и извлекает названия букетов
        """
        names = []
        lines = text.split('\n')

        for line in lines:
            line = line.strip()
            # Пропускаем пустые строки и заголовки
            if not line or any(line.lower().startswith(prefix) for prefix in self.SKIP_PREFIXES):
                continue

            # Удаляем номера списка
            if line and (line[0].isdigit() or line[0] == '-'):
                # Удаляем номер и точку или дефис
                if '.' in line:
                    line = line.split('.', 1)[1].strip()
                else:
                    line = line[1:].strip()

                # Пропускаем строки, которые стали пустыми после удаления номера
                if not line:
                    continue

            # Удаляем markdown форматирование (жирный, курсив)
            line = line.replace('**', '').replace('*', '').replace('"', '').replace("'", '').strip()

            if line:
                # Приводим к нужному формату: первое слово с заглавной, остальные строчные
                normalized_line = self._normalize_case(line)
                names.append(normalized_line)

        # Фильтруем и сортируем названия по длине для равномерного распределения
        names_by_length = {2: [], 3: [], 4: []}

        for name in names:
            word_count = len(name.split())
            if word_count in names_by_length:
                names_by_length[word_count].append(name)

        # Удаляем дубликаты в каждой группе
        for length in names_by_length:
            unique_list = []
            seen = set()
            for name in names_by_length[length]:
                if name not in seen:
                    seen.add(name)
                    unique_list.append(name)
            names_by_length[length] = unique_list

        # Объединяем названия в один список в пропорциях 2:3:4
        balanced_names = []

        # Определяем максимальное количество названий одного типа
        max_per_length = max(len(names_list) for names_list in names_by_length.values()) if any(names_by_length.values()) else 0

        # Добавляем названия по одному из каждой категории по очереди
        for i in range(max_per_length):
            for length in [2, 3, 4]:  # Проходим по длине 2, 3, 4
                if i < len(names_by_length[length]):
                    balanced_names.append(names_by_length[length][i])

        return balanced_names

    def _normalize_case(self, text: str) -> str:
        """
        Приводит текст к формату: первое слово с заглавной буквы, остальные строчные
        Например: "романтический БУКЕТ роз" -> "Романтический букет роз"
        """
        if not text:
            return text

        # Разбиваем текст на слова
        words = text.split()

        if not words:
            return text

        # Первое слово с заглавной буквы, остальные строчные
        normalized_words = [words[0].capitalize()] + [word.lower() for word in words[1:]]

        # Собираем обратно в строку
        return ' '.join(normalized_words)

    def generate_and_store(
        self,
        count: int = 500,
        characteristics: Optional[str] = None,
        occasion: Optional[str] = None,
        language: str = "russian"
    ) -> Tuple[bool, str, Optional[Dict]]:
        """
        Генерирует названия и сохраняет в базу данных
        """
        from products.models import BouquetName

        success, msg, data = self.generate(count, characteristics, occasion, language)

        if success and data:
            # Сохраняем названия в базу
            stored_count = 0
            failed_count = 0

            for name in data['names']:
                try:
                    BouquetName.objects.get_or_create(
                        name=name,
                        language=language,
                        defaults={
                            'is_approved': False
                        }
                    )
                    stored_count += 1
                except Exception as e:
                    logger.error(f"Ошибка сохранения названия '{name}': {e}")
                    failed_count += 1

            success_msg = f"Сгенерировано и сохранено {stored_count} названий для букетов"
            if failed_count > 0:
                success_msg += f", не удалось сохранить {failed_count} названий"

            return True, success_msg, data

        return success, msg, data

    def get_approved_names(
        self,
        color_tags: Optional[List[str]] = None,
        occasion_tags: Optional[List[str]] = None,
        style_tags: Optional[List[str]] = None,
        language: str = "russian",
        limit: int = 100
    ) -> List[str]:
        """
        Получает одобренные названия с фильтрацией по тегам
        """
        from products.models import BouquetName
        
        queryset = BouquetName.objects.filter(
            is_approved=True,
            language=language
        )

        if color_tags:
            queryset = queryset.filter(color_tags__name__in=color_tags)

        if occasion_tags:
            queryset = queryset.filter(occasion_tags__name__in=occasion_tags)

        if style_tags:
            queryset = queryset.filter(style_tags__name__in=style_tags)

        # Сортируем по популярности
        queryset = queryset.order_by('-usage_count')

        return list(queryset.values_list('name', flat=True)[:limit])

    def mark_as_used(self, name: str, language: str = "russian") -> None:
        """
        Увеличивает счетчик использования названия
        """
        from products.models import BouquetName
        
        BouquetName.objects.filter(
            name=name,
            language=language
        ).update(
            usage_count=models.F('usage_count') + 1
        )
