"""
Strategy management handlers (view, edit, overwrite, export).
"""
import contextlib
import logging
from typing import Dict, Any
import csv
import io

from aiogram import Router, F
from aiogram.filters import Command
from aiogram.types import Message, CallbackQuery, BufferedInputFile
from aiogram.fsm.context import FSMContext

from src.bot.states.survey import StrategyEditFSM
from src.bot.utils.text_utils import extract_day_html
from src.bot.keyboards.main import kb_strategy_edit_confirm
from src.bot.keyboards.daily import kb_daily_step
from src.database.db_methods.users import get_user_by_tg
from src.database.db_methods.strategy import get_active_strategy, ensure_today_issued
from src.database.session import SessionLocal
from src.database.models import StrategyDayState
from sqlalchemy import select

logger = logging.getLogger(__name__)
router = Router(name="strategy_management")




def _survey_from_user(user) -> Dict[str, Any]:
    """Extract survey data from user object."""
    if not user:
        return {}
    
    return {
        "gender": getattr(user, "user_gender", None),
        "age": getattr(user, "age", None),
        "city": getattr(user, "city", None),
        "practice_place": None,  # Will be loaded separately if needed
        "clients_experience": None,  # Will be loaded separately if needed
        "massage_technique": None,  # Will be loaded separately if needed
        "social_skill": None,  # Will be loaded separately if needed
        "communication_ease": None,  # Will be loaded separately if needed
    }


@router.message(Command("strategy"))
async def strategy_cmd(message: Message, state: FSMContext):
    """
    Show current strategy and offer to edit it.
    """
    tg_id = str(message.from_user.id)
    
    # Clear any existing state to prevent interference
    await state.clear()
    
    async with SessionLocal() as session:
        user = await get_user_by_tg(session, tg_id)
        if not user:
            await message.answer("Анкета не найдена. Нажми /start и пройди короткую анкету.")
            return
            
        strat = await get_active_strategy(session, user.id)
        if not strat:
            await message.answer("У тебя нет активной стратегии. Нажми /start, чтобы создать.")
            return

        text = (strat.strategy or "").strip()
        preview = text if len(text) <= 4000 else text[:3800] + "\n\n<i>…обрезано…</i>"
        await message.answer(preview)
        await message.answer("Хочешь изменить текущую стратегию?", reply_markup=kb_strategy_edit_confirm())

        await state.update_data(
            edit_mode="active_strategy",
            edit_strategy_id=strat.id,
            edit_prev_strategy=text,
            edit_user_survey=_survey_from_user(user),
        )


@router.message(Command("test"))
async def test_cmd(message: Message):
    """Test command to check if router is working."""
    await message.answer("✅ Роутер strategy_management работает!")


@router.message(Command("export30"))
async def export_last_30_days_cmd(message: Message, state: FSMContext):
    """
    Export last 30 logical days of the active strategy as CSV and send as file.
    Logical days are taken relative to current_day of the active strategy.
    """
    logger = logging.getLogger(__name__)
    logger.info(f"Export30 command received from user {message.from_user.id}")
    
    # Simple test response first
    await message.answer("✅ Команда export30 получена! Обрабатываю...")
    
    tg_id = str(message.from_user.id)
    
    async with SessionLocal() as session:
        user = await get_user_by_tg(session, tg_id)
        if not user:
            await message.answer("Анкета не найдена. Нажми /start и пройди короткую анкету.")
            return

        # Load user profile explicitly
        from src.database.models import UserProfile
        from sqlalchemy import select
        profile_result = await session.execute(
            select(UserProfile).where(UserProfile.user_id == user.id)
        )
        profile = profile_result.scalar_one_or_none()

        strat = await get_active_strategy(session, user.id)
        if not strat:
            await message.answer("У тебя нет активной стратегии. Нажми /start, чтобы создать.")
            return

        current_day = max(1, int(getattr(strat, "current_day", 1) or 1))
        from_day = max(1, current_day - 29)

        res = await session.execute(
            select(StrategyDayState)
            .where(
                StrategyDayState.strategy_id == strat.id,
                StrategyDayState.day_number >= from_day,
                StrategyDayState.day_number <= current_day,
            )
            .order_by(StrategyDayState.day_number.asc())
        )
        rows = list(res.scalars().all())

        # Prepare CSV in-memory with proper formatting
        buf = io.StringIO()
        writer = csv.writer(buf, delimiter=',', quoting=csv.QUOTE_ALL)
        
        # Helper function to extract enum values properly
        def get_enum_value(enum_obj):
            """Extract enum value, handling both enum objects and strings."""
            if enum_obj is None:
                return ""
            # If it's an enum object, get its value
            if hasattr(enum_obj, 'value'):
                return str(enum_obj.value)
            # If it's a string representation of enum, extract the value part
            if isinstance(enum_obj, str) and '.' in enum_obj:
                return enum_obj.split('.')[-1]  # Get the part after the last dot
            return str(enum_obj)
        
        # Write user information section
        writer.writerow(["=== USER INFORMATION ==="])
        writer.writerow(["user_id", "user_tg_id", "user_gender", "user_age", "user_city", "practice_place", "clients_experience", "massage_technique", "social_skill", "communication_ease"])
        
        user_data = [
            str(user.id),
            str(tg_id),
            get_enum_value(getattr(user, "user_gender", None)),
            str(getattr(user, "age", "") or ""),
            str(getattr(user, "city", "") or ""),
            get_enum_value(getattr(profile, "where_practicing", None)) if profile else "",
            get_enum_value(getattr(profile, "have_clients", None)) if profile else "",
            get_enum_value(getattr(profile, "massage_technique", None)) if profile else "",
            get_enum_value(getattr(profile, "social_skill", None)) if profile else "",
            get_enum_value(getattr(profile, "communication_ease", None)) if profile else "",
        ]
        writer.writerow(user_data)
        
        # Add empty row for separation
        writer.writerow([])
        
        # Write activities section
        writer.writerow(["=== ACTIVITIES DATA ==="])
        writer.writerow(["day_number", "status", "fail_reason", "snooze_until"]) 
        for r in rows:
            writer.writerow([
                int(getattr(r, "day_number", 0) or 0),
                get_enum_value(getattr(r, "status", None)),
                get_enum_value(getattr(r, "fail_reason", None)),
                str(getattr(r, "snooze_until", "") or ""),
            ])

        csv_bytes = buf.getvalue().encode("utf-8")
        file = BufferedInputFile(csv_bytes, filename=f"export_last_30_days_user_{user.id}.csv")
        await message.answer_document(file, caption="Выгрузка последних 30 дней по стратегии")

@router.callback_query(F.data == "strat_edit:yes")
async def strat_edit_yes(cb: CallbackQuery, state: FSMContext):
    """
    Start editing active strategy.
    """
    await cb.answer()
    await cb.message.edit_reply_markup(reply_markup=None)
    await state.set_state(StrategyEditFSM.await_feedback)
    await cb.message.answer(
        "Ок. Опиши голосом или текстом, что меняем в стратегии. "
        "Кратко и конкретно (что убрать/добавить/переориентировать). Отправь сообщение."
    )


@router.callback_query(F.data == "strat_edit:no")
async def strat_edit_no(cb: CallbackQuery, state: FSMContext):
    """
    Cancel strategy editing.
    """
    await cb.answer()
    await cb.message.edit_reply_markup(reply_markup=None)
    
    # Clear state FIRST to prevent interference with other commands
    await state.clear()
    # Also clear FSM state to ensure clean state
    await state.set_state(None)
    
    await cb.message.answer("Оставил стратегию без изменений 👍")


@router.message(StrategyEditFSM.await_feedback, F.text)
async def review_feedback_text(message: Message, state: FSMContext):
    """
    Handle text feedback for strategy revision.
    """
    feedback = (message.text or "").strip()
    
    # Ignore commands - they should be handled by command handlers
    if feedback.startswith("/"):
        await state.clear()
        await state.set_state(None)
        # Let the command handler process the command
        return
        
    if not feedback or len(feedback) < 3:
        await message.answer("Слишком коротко. Напиши конкретнее или отправь голосовым сообщением.")
        return

    data = await state.get_data()
    edit_mode = data.get("edit_mode")

    # Additional check: only process if we're actually waiting for feedback
    if edit_mode != "active_strategy":
        await state.clear()
        await state.set_state(None)
        return
        
    # Additional check: make sure we have the required data
    if not data.get("edit_strategy_id") or not data.get("edit_prev_strategy"):
        await state.clear()
        await state.set_state(None)
        return
        
    # Additional check: make sure we're not in a loop
    if data.get("edit_preview_strategy"):
        # We already processed feedback, ignore further messages
        await state.clear()
        await state.set_state(None)
        return

    # Import here to avoid circular imports
    from src.bot.services.strategy_service import StrategyService
    from src.bot.services.steps_sanitizer import StepsSanitizer
    from src.bot.utils.text_utils import strategy_without_day_blocks
    from src.bot.keyboards.main import kb_strategy_overwrite_confirm

    survey = data.get("edit_user_survey") or {}
    prev_strategy = data.get("edit_prev_strategy") or ""

    await message.answer("Перерабатываю стратегию с учётом твоих правок…")
    
    strategy_service = StrategyService()
    sanitizer = StepsSanitizer()
    
    new_strategy, new_steps = await strategy_service.rebuild_strategy_and_steps_with_feedback(
        survey, prev_strategy, feedback
    )
    new_steps = sanitizer.sanitize_steps(new_steps, survey)
    
    clean_strategy = strategy_without_day_blocks(new_strategy)

    await state.update_data(
        edit_preview_strategy=clean_strategy,
        edit_preview_steps=new_steps,
    )

    await message.answer(clean_strategy or "Стратегия пустая.")
    await message.answer("Перезаписать текущую стратегию этой версией?", reply_markup=kb_strategy_overwrite_confirm())


@router.message(StrategyEditFSM.await_feedback, F.voice | F.audio | F.video_note)
async def review_feedback_voice(message: Message, state: FSMContext):
    """
    Handle voice/audio/video_note feedback for strategy revision.
    """
    await message.answer("Ок, распознаю голос и учту правки…")
    
    # Import here to avoid circular imports
    from src.bot.services.strategy_service import StrategyService
    from src.bot.services.steps_sanitizer import StepsSanitizer
    from src.bot.utils.text_utils import strategy_without_day_blocks
    from src.bot.keyboards.main import kb_strategy_overwrite_confirm
    import asyncio
    import io
    import os
    import aiohttp
    from aiogram.types import ChatAction
    
    # Simple transcription using OpenAI Whisper
    try:
        if message.voice:
            file_id = message.voice.file_id
            filename = f"voice_{message.voice.file_unique_id or 'tg'}.ogg"
        elif message.audio:
            file_id = message.audio.file_id
            filename = message.audio.file_name or "audio.mp3"
        else:
            file_id = message.video_note.file_id
            filename = "video_note.mp4"
        
        # Download file
        buf = io.BytesIO()
        file = await message.bot.get_file(file_id)
        await message.bot.download_file(file.file_path, buf)
        audio_bytes = buf.getvalue()
        
        # Transcribe with OpenAI Whisper
        api_key = os.getenv("OPENAI_API_KEY")
        if not api_key:
            await message.answer("Не настроен API ключ для распознавания голоса. Напиши текстом, пожалуйста.")
            return
            
        async with aiohttp.ClientSession() as session:
            data = aiohttp.FormData()
            data.add_field('file', audio_bytes, filename=filename, content_type='audio/ogg')
            data.add_field('model', 'whisper-1')
            data.add_field('language', 'ru')
            
            async with session.post(
                'https://api.openai.com/v1/audio/transcriptions',
                headers={'Authorization': f'Bearer {api_key}'},
                data=data
            ) as resp:
                if resp.status != 200:
                    await message.answer("Не удалось распознать голос. Попробуй ещё раз или отправь текстом 🙏")
                    return
                result = await resp.json()
                feedback = result.get('text', '').strip()
        
        if not feedback or len(feedback) < 3:
            await message.answer("Слишком коротко. Скажи конкретнее, что поменять.")
            return
            
        # Process feedback same as text
        data = await state.get_data()
        edit_mode = data.get("edit_mode")
        
        if edit_mode != "active_strategy":
            await state.clear()
            await state.set_state(None)
            return
            
        if not data.get("edit_strategy_id") or not data.get("edit_prev_strategy"):
            await state.clear()
            await state.set_state(None)
            return
            
        if data.get("edit_preview_strategy"):
            await state.clear()
            await state.set_state(None)
            return

        survey = data.get("edit_user_survey") or {}
        prev_strategy = data.get("edit_prev_strategy") or ""

        await message.answer("Перерабатываю стратегию с учётом твоих правок…")
        
        strategy_service = StrategyService()
        sanitizer = StepsSanitizer()
        
        new_strategy, new_steps = await strategy_service.rebuild_strategy_and_steps_with_feedback(
            survey, prev_strategy, feedback
        )
        new_steps = sanitizer.sanitize_steps(new_steps, survey)
        
        clean_strategy = strategy_without_day_blocks(new_strategy)

        await state.update_data(
            edit_preview_strategy=clean_strategy,
            edit_preview_steps=new_steps,
        )

        await message.answer(clean_strategy or "Стратегия пустая.")
        await message.answer("Перезаписать текущую стратегию этой версией?", reply_markup=kb_strategy_overwrite_confirm())
        
    except Exception as e:
        logger.exception("[strategy_management] voice feedback error: %s", e)
        await message.answer("Не удалось распознать голос. Попробуй ещё раз или отправь текстом 🙏")


@router.callback_query(F.data == "strat_overwrite:yes")
async def strat_overwrite_yes(cb: CallbackQuery, state: FSMContext):
    """
    Confirm strategy overwrite.
    """
    data = await state.get_data()
    strategy_id = data.get("edit_strategy_id")
    new_text = data.get("edit_preview_strategy") or ""
    new_steps = data.get("edit_preview_steps") or {}

    if not strategy_id or not new_text:
        await cb.answer("Нет данных для перезаписи.", show_alert=True)
        return

    await cb.answer("Перезаписываю…")
    
    # Import here to avoid circular imports
    from src.database.models import Strategy, StrategyDayState, StrategyStatus
    from sqlalchemy import delete
    
    async with SessionLocal() as session:
        # Get strategy
        strategy = await session.get(Strategy, strategy_id)
        if not strategy:
            await cb.answer("Стратегия не найдена.", show_alert=True)
            return
            
        # Drop day states if needed
        await session.execute(
            delete(StrategyDayState).where(StrategyDayState.strategy_id == strategy_id)
        )
        
        # Update strategy
        strategy.strategy = new_text
        strategy.steps = new_steps or {}
        strategy.status = StrategyStatus.active
        strategy.current_day = 1
        
        await session.flush()
        await ensure_today_issued(session, strategy_id)

    with contextlib.suppress(Exception):
        await cb.message.edit_reply_markup(reply_markup=None)

    # Clear state FIRST to prevent interference with future commands
    await state.clear()
    # Also clear FSM state to ensure clean state
    await state.set_state(None)
    
    await cb.message.answer("Готово. Текущая стратегия обновлена.")
    
    # Issue first task of updated strategy
    first_day_html = extract_day_html(new_steps, 1)
    await cb.message.answer(first_day_html or "Сегодняшний шаг недоступен.", reply_markup=kb_daily_step())


@router.callback_query(F.data == "strat_overwrite:no")
async def strat_overwrite_no(cb: CallbackQuery, state: FSMContext):
    """
    Cancel strategy overwrite.
    """
    await cb.answer()
    with contextlib.suppress(Exception):
        await cb.message.edit_reply_markup(reply_markup=None)
    
    # Clear state FIRST to prevent interference with future commands
    await state.clear()
    # Also clear FSM state to ensure clean state
    await state.set_state(None)
    
    await cb.message.answer("Оставил текущую стратегию без изменений 👍")


async def _overwrite_strategy(
    session,
    *,
    strategy_id: int,
    new_strategy_text: str,
    new_steps: Dict[str, Any],
    reset_to_day: int = 1,
    drop_day_states: bool = True,
):
    """
    Overwrite strategy with new content.
    
    Args:
        session: Database session
        strategy_id: Strategy ID to overwrite
        new_strategy_text: New strategy text
        new_steps: New steps dictionary
        reset_to_day: Day to reset to
        drop_day_states: Whether to drop existing day states
    """
    from src.database.models import Strategy, StrategyDayState
    from src.database.enums import StrategyStatus
    from sqlalchemy import delete
    from src.database.db_methods.strategy import _get_strategy, _max_day, _ensure_day_state
    
    strat = await _get_strategy(session, strategy_id)
    if not strat:
        raise ValueError("стратегия не найдена")

    if drop_day_states:
        await session.execute(
            delete(StrategyDayState).where(StrategyDayState.strategy_id == strat.id)
        )

    strat.strategy = new_strategy_text
    strat.steps = new_steps or {}
    strat.status = StrategyStatus.active
    strat.current_day = reset_to_day

    await session.flush()

    if _max_day(strat.steps) >= reset_to_day:
        await _ensure_day_state(session, strat.id, reset_to_day)

    return strat
