import json
import logging
import os
from typing import Any

from celery_worker.celery_app import celery_app

# Настраиваем логгер для задач
logger = logging.getLogger(__name__)
from celery_worker.database import (
    SyncResumeRepository,
    SyncVacancyRepository,
    get_sync_session,
)
from rag.llm.model import ResumeParser
from rag.registry import registry

# Импортируем новые задачи анализа интервью


def generate_interview_plan(
    resume_id: int, combined_data: dict[str, Any]
) -> dict[str, Any]:
    """Генерирует план интервью на основе резюме и вакансии"""
    try:
        # Получаем данные о вакансии из БД
        with get_sync_session() as session:
            resume_repo = SyncResumeRepository(session)
            vacancy_repo = SyncVacancyRepository(session)

            resume_record = resume_repo.get_by_id(resume_id)
            if not resume_record:
                return {
                    "is_suitable": False,
                    "rejection_reason": "Резюме не найдено в БД",
                }

            # Получаем данные вакансии
            vacancy_record = None
            if resume_record.vacancy_id:
                vacancy_record = vacancy_repo.get_by_id(resume_record.vacancy_id)

            if not vacancy_record:
                return {"is_suitable": False, "rejection_reason": "Вакансия не найдена"}

            vacancy_data = {
                "title": vacancy_record.title,
                "description": vacancy_record.description,
                "key_skills": vacancy_record.key_skills,
                "experience": vacancy_record.experience,
                "area_name": vacancy_record.area_name,
                "professional_roles": vacancy_record.professional_roles,
            }

        # Сначала проверяем соответствие резюме и вакансии через LLM
        chat_model = registry.get_chat_model()

        # Формируем опыт кандидата
        experience_map = {
            "noExperience": "Без опыта",
            "between1And3": "1-3 года",
            "between3And6": "3-6 лет",
            "moreThan6": "Более 6 лет",
        }

        compatibility_prompt = f"""
        Проанализируй соответствие кандидата вакансии и определи, стоит ли проводить интервью.
        
        КЛЮЧЕВЫЕ КРИТЕРИИ ОТКЛОНЕНИЯ:
        1. Несоответствие профессиональной сферы — опыт и навыки кандидата не относятся к области деятельности, связанной с вакансией.
        2. Несоответствие уровня и фокуса позиции — текущая или предыдущая должность кандидата существенно отличается по направлению или уровню ответственности от требований вакансии. Допускаются смежные переходы (например, переход из fullstack в frontend или переход кандидата уровня senior на позицию middle/junior). 
        КЛЮЧЕВЫЕ КРИТЕРИИ ДОПУСКА:
        3. Остальные показатели кандидата примерно соответствуют вакансии: скиллы кандидата похожи или смежны вакансионным, опыт попадает в указанных промежуток
        4. Учитывай опыт с аналогичными, похожими, смежными технологиями
        5. Когда смотришь на вакансию и кандидата не учитывай строгие слова, такие как "Требования", "Ключевые" и тп. Это лишь маркеры, 
        но не оценочные указатели
        6. Если есть спорные вопросы соответствия, лучше допустить к собеседованию и уточнить их там
        
        КАНДИДАТ: 
        - Имя: {combined_data.get("name", "Не указано")}
        - Навыки: {", ".join(combined_data.get("skills", []))}
        - Общий опыт: {combined_data.get("total_years", 0)} лет
        - Образование: {combined_data.get("education", "Не указано")}
        - Про работу: {combined_data.get("experience", "Не указано")}
        - Саммари: {combined_data.get("summary", "Не указано")}
        
        ВАКАНСИЯ:
        - Должность: {vacancy_data["title"]}
        - Описание: {vacancy_data["description"]}...
        - Ключевые навыки: {vacancy_data["key_skills"] or "Не указаны"}
        - Требуемый опыт: {experience_map.get(vacancy_data["experience"], "Не указан")}
        - Профессиональные роли: {vacancy_data["professional_roles"] or "Не указаны"}
        

        Верни ответ в JSON формате:
        {{
            "is_suitable": true/false,
            "rejection_reason": "Конкретная подробная причина отклонения с цитированием (если is_suitable=false)",
        }}
        """

        from langchain.schema import HumanMessage, SystemMessage

        compatibility_messages = [
            SystemMessage(
                content="Ты эксперт по подбору персонала. Анализируй соответствие кандидатов вакансиям строго и объективно."
            ),
            HumanMessage(content=compatibility_prompt),
        ]

        compatibility_response = chat_model.get_llm().invoke(compatibility_messages)
        compatibility_text = compatibility_response.content.strip()

        # Парсим ответ о соответствии
        compatibility_result = None
        if compatibility_text.startswith("{") and compatibility_text.endswith("}"):
            compatibility_result = json.loads(compatibility_text)
        else:
            # Ищем JSON в тексте
            start = compatibility_text.find("{")
            end = compatibility_text.rfind("}") + 1
            if start != -1 and end > start:
                compatibility_result = json.loads(compatibility_text[start:end])

        # Если кандидат не подходит - возвращаем результат отклонения
        if not compatibility_result or not compatibility_result.get(
            "is_suitable", True
        ):
            return {
                "is_suitable": False,
                "rejection_reason": compatibility_result.get(
                    "rejection_reason", "Кандидат не соответствует требованиям вакансии"
                )
                if compatibility_result
                else "Ошибка анализа соответствия",
                "match_details": compatibility_result,
            }

        # Если кандидат подходит - генерируем план интервью
        plan_prompt = f"""
        Создай детальный план интервью для кандидата на основе его резюме и требований вакансии на 45 МИНУТ.
        
        РЕЗЮМЕ КАНДИДАТА:
        - Имя: {combined_data.get("name", "Не указано")}
        - Навыки: {", ".join(combined_data.get("skills", []))}
        - Опыт: {combined_data.get("total_years", 0)} лет
        - Образование: {combined_data.get("education", "Не указано")}
        
        ВАКАНСИЯ:
        - Должность: {vacancy_data["title"]}
        - Описание: {vacancy_data["description"]}...
        - Ключевые навыки: {vacancy_data["key_skills"] or "Не указаны"}
        - Требуемый опыт: {experience_map.get(vacancy_data["experience"], "Не указан")}
        
        Создай план интервью в формате JSON:
        {{
            "interview_structure": {{
                "duration_minutes": 45,
                "greeting": "Краткое приветствие и знакомство (3 мин)",
                "sections": [
                    {{
                        "name": "Знакомство с кандидатом",
                        "duration_minutes": 5,
                        "questions": ["Расскажи о себе", "Что привлекло в этой позиции?"]
                    }},
                    {{
                        "name": "Технические навыки",
                        "duration_minutes": 20,
                        "questions": ["Опыт с Python", "Работа с базами данных"]
                    }},
                    {{
                        "name": "Опыт и проекты", 
                        "duration_minutes": 15,
                        "questions": ["Расскажи о сложном проекте", "Как решаешь проблемы?"]
                    }},
                    {{
                        "name": "Вопросы кандидата",
                        "duration_minutes": 2,
                        "questions": ["Есть ли вопросы ко мне?"]
                    }}
                ]
            }},
            "focus_areas": ["technical_skills", "problem_solving", "cultural_fit"],
            "key_evaluation_points": [
                "Глубина знаний Python",
                "Опыт командной работы", 
                "Мотивация к изучению нового"
            ],
            "red_flags_to_check": [Шаблонные ответы,  уклонения от вопросов, расхождение в стаже],
            "personalization_notes": "Кандидат имеет хороший технический опыт"
        }}
        """

        from langchain.schema import HumanMessage, SystemMessage

        messages = [
            SystemMessage(
                content="Ты HR эксперт по планированию интервью. Создавай структурированные планы."
            ),
            HumanMessage(content=plan_prompt),
        ]

        response = chat_model.get_llm().invoke(messages)
        response_text = response.content.strip()

        # Парсим JSON ответ
        interview_plan = None
        if response_text.startswith("{") and response_text.endswith("}"):
            interview_plan = json.loads(response_text)
        else:
            # Ищем JSON в тексте
            start = response_text.find("{")
            end = response_text.rfind("}") + 1
            if start != -1 and end > start:
                interview_plan = json.loads(response_text[start:end])

        if interview_plan:
            # Добавляем информацию о том, что кандидат подходит
            interview_plan["is_suitable"] = True
            interview_plan["match_details"] = compatibility_result
            return interview_plan

        return {
            "is_suitable": True,
            "match_details": compatibility_result,
            "error": "Не удалось сгенерировать план интервью",
        }

    except Exception as e:
        logger.error(f"Ошибка генерации плана интервью: {str(e)}", exc_info=True)
        return None


@celery_app.task(bind=True)
def parse_resume_task(self, resume_id: str, file_path: str):
    """
    Асинхронная задача парсинга резюме

    Args:
        resume_id: ID резюме
        file_path: Путь к PDF файлу резюме
    """
    logger.info(f"=== НАЧАЛО ОБРАБОТКИ РЕЗЮМЕ {resume_id} ===")
    logger.info(f"Путь к файлу: {file_path}")

    try:
        # Шаг 0: Обновляем статус в БД - начали парсинг
        logger.info(f"Шаг 0: Обновляем статус резюме {resume_id} на 'parsing'")
        with get_sync_session() as session:
            repo = SyncResumeRepository(session)
            repo.update_status(int(resume_id), "parsing")
        logger.info(f"Статус резюме {resume_id} успешно обновлен на 'parsing'")

        # Обновляем статус задачи
        logger.info(f"Обновляем состояние Celery задачи на PENDING")
        self.update_state(
            state="PENDING",
            meta={"status": "Начинаем парсинг резюме...", "progress": 10},
        )
        logger.info(f"Состояние Celery задачи обновлено")

        # Инициализируем модели из registry
        logger.info(f"Шаг 1: Инициализируем модели из registry")
        try:
            logger.info("Получаем chat_model из registry")
            chat_model = registry.get_chat_model()
            logger.info("Chat model успешно получен")
            
            logger.info("Получаем vector_store из registry")
            vector_store = registry.get_vector_store()
            logger.info("Vector store успешно получен")
        except Exception as e:
            logger.error(f"ОШИБКА при инициализации моделей: {str(e)}", exc_info=True)
            # Обновляем статус в БД - ошибка инициализации
            with get_sync_session() as session:
                repo = SyncResumeRepository(session)
                repo.update_status(
                    int(resume_id),
                    "failed",
                    error_message=f"Ошибка инициализации моделей: {str(e)}",
                )
            raise RuntimeError(f"Ошибка инициализации моделей: {str(e)}")

        # Шаг 2: Парсинг резюме
        logger.info(f"Шаг 2: Начинаем парсинг резюме")
        self.update_state(
            state="PROGRESS",
            meta={"status": "Извлекаем текст из PDF...", "progress": 20},
        )
        logger.info(f"Состояние Celery обновлено на PROGRESS (20%)")

        logger.info(f"Создаем ResumeParser")
        parser = ResumeParser(chat_model)
        logger.info(f"ResumeParser создан успешно")

        logger.info(f"Проверяем существование файла: {file_path}")
        if not os.path.exists(file_path):
            logger.error(f"ФАЙЛ НЕ НАЙДЕН: {file_path}")
            # Обновляем статус в БД - файл не найден
            with get_sync_session() as session:
                repo = SyncResumeRepository(session)
                repo.update_status(
                    int(resume_id),
                    "failed",
                    error_message=f"Файл не найден: {file_path}",
                )
            logger.info(f"Статус резюме {resume_id} обновлен на 'failed' в БД")
            raise FileNotFoundError(f"Файл не найден: {file_path}")

        logger.info(f"Файл существует, начинаем парсинг")
        parsed_resume = parser.parse_resume_from_file(file_path)
        logger.info(f"Парсинг резюме завершен, получены данные: {list(parsed_resume.keys())}")

        # Получаем оригинальные данные из формы
        logger.info(f"Шаг 3: Получаем данные резюме из БД")
        with get_sync_session() as session:
            repo = SyncResumeRepository(session)
            resume_record = repo.get_by_id(int(resume_id))
            if not resume_record:
                logger.error(f"РЕЗЮМЕ С ID {resume_id} НЕ НАЙДЕНО В БД")
                raise ValueError(f"Резюме с ID {resume_id} не найдено в базе данных")

            # Извлекаем нужные данные пока сессия активна
            applicant_name = resume_record.applicant_name
            applicant_email = resume_record.applicant_email
            applicant_phone = resume_record.applicant_phone
            logger.info(f"Данные резюме получены: name={applicant_name}, email={applicant_email}, phone={applicant_phone}")

        # Создаем комбинированные данные: навыки и опыт из парсинга, контакты из формы
        logger.info(f"Шаг 4: Объединяем данные из парсинга и формы")
        combined_data = parsed_resume.copy()
        combined_data["name"] = applicant_name or parsed_resume.get("name", "")
        combined_data["email"] = applicant_email or parsed_resume.get("email", "")
        combined_data["phone"] = applicant_phone or parsed_resume.get("phone", "")
        logger.info(f"Комбинированные данные подготовлены")

        # Шаг 5: Векторизация и сохранение в Milvus
        logger.info(f"Шаг 5: Векторизация и сохранение в Milvus")
        self.update_state(
            state="PENDING",
            meta={"status": "Сохраняем в векторную базу...", "progress": 60},
        )
        logger.info(f"Состояние Celery обновлено на 60%")

        logger.info(f"Добавляем профиль кандидата в vector store")
        vector_store.add_candidate_profile(str(resume_id), combined_data)
        logger.info(f"Профиль кандидата добавлен в vector store")

        # Шаг 6: Обновляем статус в PostgreSQL
        logger.info(f"Шаг 6: Подготовка к обновлению статуса в БД")
        self.update_state(
            state="PENDING",
            meta={"status": "Обновляем статус в базе данных...", "progress": 85},
        )
        logger.info(f"Состояние Celery обновлено на 85%")

        # Шаг 7: Генерируем план интервью
        logger.info(f"Шаг 7: Генерация плана интервью")
        self.update_state(
            state="PENDING",
            meta={"status": "Генерируем план интервью...", "progress": 90},
        )
        logger.info(f"Состояние Celery обновлено на 90%")

        logger.info(f"Вызываем generate_interview_plan для резюме {resume_id}")
        interview_plan = generate_interview_plan(int(resume_id), combined_data)
        logger.info(f"План интервью сгенерирован: {interview_plan is not None}")

        logger.info(f"Шаг 8: Обновляем статус в БД на основе плана интервью")
        with get_sync_session() as session:
            repo = SyncResumeRepository(session)

            # Проверяем результат генерации плана интервью
            logger.info(f"Анализируем план интервью для резюме {resume_id}")
            logger.info(f"План интервью: {interview_plan}")
            
            if interview_plan and interview_plan.get("is_suitable", True):
                logger.info(f"Кандидат подходит, обновляем статус на 'parsed'")
                # Кандидат подходит - обновляем статус на parsed
                repo.update_status(int(resume_id), "parsed", parsed_data=combined_data)
                logger.info(f"Статус резюме {resume_id} обновлен на 'parsed'")
                
                # Сохраняем план интервью
                logger.info(f"Сохраняем план интервью для резюме {resume_id}")
                repo.update_interview_plan(int(resume_id), interview_plan)
                logger.info(f"План интервью сохранен")
            else:
                logger.info(f"Кандидат НЕ подходит, отклоняем")
                # Кандидат не подходит - отклоняем
                rejection_reason = (
                    interview_plan.get(
                        "rejection_reason", "Не соответствует требованиям вакансии"
                    )
                    if interview_plan
                    else "Ошибка анализа соответствия"
                )
                logger.info(f"Причина отклонения: {rejection_reason}")
                repo.update_status(
                    int(resume_id),
                    "rejected",
                    parsed_data=combined_data,
                    rejection_reason=rejection_reason,
                )
                logger.info(f"Статус резюме {resume_id} обновлен на 'rejected'")

                # Завершаем с информацией об отклонении
                logger.info(f"Обновляем состояние Celery на SUCCESS (отклонен)")
                self.update_state(
                    state="SUCCESS",
                    meta={
                        "status": f"Резюме обработано, но кандидат отклонен: {rejection_reason}",
                        "progress": 100,
                        "result": combined_data,
                        "rejected": True,
                        "rejection_reason": rejection_reason,
                    },
                )
                logger.info(f"=== ЗАВЕРШЕНИЕ ОБРАБОТКИ РЕЗЮМЕ {resume_id} (ОТКЛОНЕН) ===")

                return {
                    "resume_id": resume_id,
                    "status": "rejected",
                    "parsed_data": combined_data,
                    "rejection_reason": rejection_reason,
                }

        # Завершено успешно
        logger.info(f"Обновляем состояние Celery на SUCCESS (принят)")
        self.update_state(
            state="SUCCESS",
            meta={
                "status": "Резюме успешно обработано и план интервью готов",
                "progress": 100,
                "result": combined_data,
            },
        )
        logger.info(f"=== УСПЕШНОЕ ЗАВЕРШЕНИЕ ОБРАБОТКИ РЕЗЮМЕ {resume_id} ===")

        return {
            "resume_id": resume_id,
            "status": "completed",
            "parsed_data": combined_data,
        }

    except Exception as e:
        error_message = str(e)
        logger.error(f"Ошибка при обработке резюме {resume_id}: {error_message}", exc_info=True)
        
        # В случае ошибки
        self.update_state(
            state="FAILURE",
            meta={
                "status": f"Ошибка при обработке резюме: {error_message}",
                "progress": 0,
                "error": error_message,
            },
        )

        # Обновляем статус в БД как failed
        try:
            with get_sync_session() as session:
                repo = SyncResumeRepository(session)
                repo.update_status(int(resume_id), "failed", error_message=error_message)
        except Exception as db_error:
            logger.error(f"Ошибка при обновлении статуса в БД: {str(db_error)}", exc_info=True)

        # Возвращаем стандартное исключение вместо re-raise
        return {
            "resume_id": resume_id,
            "status": "failed",
            "error": error_message,
        }


# Функция больше не нужна - используем SyncResumeRepository напрямую


@celery_app.task(bind=True)
def generate_interview_questions_task(self, resume_id: str, job_description: str):
    """
    Генерация персонализированных вопросов для интервью на основе резюме и описания вакансии

    Args:
        resume_id: ID резюме
        job_description: Описание вакансии
    """
    try:
        self.update_state(
            state="PENDING",
            meta={"status": "Начинаем генерацию вопросов...", "progress": 10},
        )

        # Инициализируем модели
        try:
            chat_model = registry.get_chat_model()
            vector_store = registry.get_vector_store()
        except Exception as e:
            raise RuntimeError(f"Ошибка инициализации моделей: {str(e)}")

        # Шаг 1: Получить parsed резюме из базы данных
        self.update_state(
            state="PENDING",
            meta={"status": "Получаем данные резюме...", "progress": 20},
        )

        with get_sync_session() as session:
            repo = SyncResumeRepository(session)
            resume = repo.get_by_id(int(resume_id))

            if not resume:
                raise Exception(f"Резюме с ID {resume_id} не найдено")

            if not resume.parsed_data:
                raise Exception(f"Резюме {resume_id} еще не обработано")

        # Шаг 2: Получить похожие кандидатов из Milvus для анализа
        self.update_state(
            state="PENDING",
            meta={"status": "Анализируем профиль кандидата...", "progress": 40},
        )

        candidate_skills = " ".join(resume.parsed_data.get("skills", []))
        similar_candidates = vector_store.search_similar_candidates(
            candidate_skills, k=3
        )

        # Шаг 3: Сгенерировать персонализированные вопросы через LLM
        self.update_state(
            state="PENDING",
            meta={"status": "Генерируем вопросы для интервью...", "progress": 70},
        )

        questions_prompt = f"""
        Сгенерируй 10 персонализированных вопросов для интервью кандидата на основе его резюме и описания вакансии.
        
        РЕЗЮМЕ КАНДИДАТА:
        Имя: {resume.parsed_data.get("name", "Не указано")}
        Навыки: {", ".join(resume.parsed_data.get("skills", []))}
        Опыт работы: {resume.parsed_data.get("total_years", 0)} лет
        Образование: {resume.parsed_data.get("education", "Не указано")}
        
        ОПИСАНИЕ ВАКАНСИИ:
        {job_description}
        
        ИНСТРУКЦИИ:
        1. Задавай вопросы, которые помогут оценить технические навыки кандидата
        2. Включи вопросы о конкретном опыте работы из резюме
        3. Добавь вопросы на соответствие требованиям вакансии
        4. Включи 2-3 поведенческих вопроса
        5. Верни ответ в JSON формате
        
        Формат ответа:
        {{
          "questions": [
            {{
              "id": 1,
              "category": "technical|experience|behavioral|vacancy_specific",
              "question": "Текст вопроса",
              "reasoning": "Почему этот вопрос важен для данного кандидата"
            }}
          ]
        }}
        """

        from langchain.schema import HumanMessage, SystemMessage

        messages = [
            SystemMessage(
                content="Ты эксперт по проведению технических интервью. Генерируй качественные, персонализированные вопросы."
            ),
            HumanMessage(content=questions_prompt),
        ]

        response = chat_model.get_llm().invoke(messages)

        # Парсим ответ
        import json

        response_text = response.content.strip()

        # Извлекаем JSON из ответа
        if response_text.startswith("{") and response_text.endswith("}"):
            questions_data = json.loads(response_text)
        else:
            # Ищем JSON внутри текста
            start = response_text.find("{")
            end = response_text.rfind("}") + 1
            if start != -1 and end > start:
                json_str = response_text[start:end]
                questions_data = json.loads(json_str)
            else:
                raise ValueError("JSON не найден в ответе LLM")

        # Шаг 4: Сохранить вопросы в notes резюме (пока так, потом можно создать отдельную таблицу)
        self.update_state(
            state="PENDING", meta={"status": "Сохраняем вопросы...", "progress": 90}
        )

        with get_sync_session() as session:
            repo = SyncResumeRepository(session)
            resume = repo.get_by_id(int(resume_id))

            if resume:
                # Сохраняем вопросы в notes (временно)
                existing_notes = resume.notes or ""
                interview_questions = json.dumps(
                    questions_data, ensure_ascii=False, indent=2
                )
                resume.notes = (
                    f"{existing_notes}\n\nINTERVIEW QUESTIONS:\n{interview_questions}"
                )
                from datetime import datetime

                resume.updated_at = datetime.utcnow()

                session.add(resume)

        # Завершено успешно
        self.update_state(
            state="SUCCESS",
            meta={
                "status": "Вопросы для интервью успешно сгенерированы",
                "progress": 100,
                "result": questions_data,
            },
        )

        return {
            "resume_id": resume_id,
            "status": "questions_generated",
            "questions": questions_data["questions"],
        }

    except Exception as e:
        # В случае ошибки
        self.update_state(
            state="FAILURE",
            meta={
                "status": f"Ошибка при генерации вопросов: {str(e)}",
                "progress": 0,
                "error": str(e),
            },
        )
        raise Exception(f"Ошибка при генерации вопросов: {str(e)}")


@celery_app.task(bind=True)
def parse_vacancy_task(
    self, file_content_base64: str, filename: str, create_vacancy: bool = False
):
    """
    Асинхронная задача парсинга вакансии из файла

    Args:
        file_content_base64: Содержимое файла в base64
        filename: Имя файла для определения формата
        create_vacancy: Создать вакансию в БД после парсинга
    """
    try:
        import base64

        from app.models.vacancy import VacancyCreate
        from app.services.vacancy_parser_service import vacancy_parser_service

        # Обновляем статус задачи
        self.update_state(
            state="PENDING",
            meta={"status": "Начинаем парсинг вакансии...", "progress": 10},
        )

        # Декодируем содержимое файла
        file_content = base64.b64decode(file_content_base64)

        # Шаг 1: Извлечение текста из файла
        self.update_state(
            state="PROGRESS",
            meta={"status": "Извлекаем текст из файла...", "progress": 30},
        )

        raw_text = vacancy_parser_service.extract_text_from_file(file_content, filename)

        if not raw_text.strip():
            raise ValueError("Не удалось извлечь текст из файла")

        # Шаг 2: Парсинг с помощью AI
        self.update_state(
            state="PROGRESS",
            meta={"status": "Обрабатываем текст с помощью AI...", "progress": 70},
        )

        import asyncio

        parsed_data = asyncio.run(
            vacancy_parser_service.parse_vacancy_with_ai(raw_text)
        )

        # Шаг 3: Создание вакансии (если требуется)
        created_vacancy = None
        print(
            f"create_vacancy parameter: {create_vacancy}, type: {type(create_vacancy)}"
        )

        if create_vacancy:
            self.update_state(
                state="PROGRESS",
                meta={"status": "Создаем вакансию в базе данных...", "progress": 90},
            )

            try:
                print(f"Parsed data for vacancy creation: {parsed_data}")
                vacancy_create = VacancyCreate(**parsed_data)
                print(f"VacancyCreate object created successfully: {vacancy_create}")

                with get_sync_session() as session:
                    vacancy_repo = SyncVacancyRepository(session)
                    created_vacancy = vacancy_repo.create_vacancy(vacancy_create)
                    print(
                        f"Vacancy created with ID: {created_vacancy.id if created_vacancy else 'None'}"
                    )

            except Exception as e:
                import traceback

                error_details = traceback.format_exc()
                print(f"Error creating vacancy: {str(e)}")
                print(f"Full traceback: {error_details}")

                # Возвращаем парсинг, но предупреждаем об ошибке создания
                self.update_state(
                    state="SUCCESS",
                    meta={
                        "status": f"Парсинг выполнен, но ошибка при создании вакансии: {str(e)}",
                        "progress": 100,
                        "result": parsed_data,
                        "warning": f"Ошибка создания вакансии: {str(e)}",
                    },
                )

                return {
                    "status": "parsed_with_warning",
                    "parsed_data": parsed_data,
                    "warning": f"Ошибка при создании вакансии: {str(e)}",
                }

        # Завершено успешно
        response_message = "Парсинг выполнен успешно"
        if created_vacancy:
            response_message += f". Вакансия создана с ID: {created_vacancy.id}"

        self.update_state(
            state="SUCCESS",
            meta={
                "status": response_message,
                "progress": 100,
                "result": parsed_data,
                "vacancy_id": created_vacancy.id if created_vacancy else None,
            },
        )

        return {
            "status": "completed",
            "parsed_data": parsed_data,
            "vacancy_id": created_vacancy.id if created_vacancy else None,
            "message": response_message,
        }

    except Exception as e:
        # В случае ошибки
        self.update_state(
            state="FAILURE",
            meta={
                "status": f"Ошибка при парсинге вакансии: {str(e)}",
                "progress": 0,
                "error": str(e),
            },
        )

        raise Exception(f"Ошибка при парсинге вакансии: {str(e)}")


@celery_app.task(bind=True)
def generate_pdf_report_task(
        self,
        report_data: dict,
        candidate_name: str = None,
        position: str = None,
        resume_file_url: str = None,
):
    """
    Асинхронная задача для генерации PDF отчета по интервью с использованием PDFShift API

    Args:
        report_data: Словарь с данными отчета InterviewReport
        candidate_name: Имя кандидата
        position: Позиция
        resume_file_url: URL резюме
    """
    try:
        import asyncio
        import requests
        from datetime import datetime
        from app.core.config import settings
        from celery_worker.database import (
            SyncInterviewReportRepository,
            get_sync_session,
        )

        # Обновляем статус задачи
        self.update_state(
            state="PENDING",
            meta={"status": "Начинаем генерацию PDF отчета...", "progress": 10},
        )

        # Генерируем HTML контент отчета из Jinja шаблона
        self.update_state(
            state="PROGRESS",
            meta={"status": "Подготавливаем HTML отчета...", "progress": 20},
        )

        # Создаем объект InterviewReport для использования в сервисе
        clean_report_data = report_data.copy()
        clean_report_data.pop('created_at', None)
        clean_report_data.pop('updated_at', None)

        from app.models.interview_report import InterviewReport
        mock_report = InterviewReport(**clean_report_data)

        # Генерируем HTML через сервис
        def run_html_generation():
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                from app.services.pdf_report_service import pdf_report_service
                return loop.run_until_complete(
                    pdf_report_service.generate_html_report(
                        mock_report, candidate_name, position, resume_file_url
                    )
                )
            finally:
                loop.close()

        html_content = run_html_generation()

        # Генерируем PDF через Cloudmersive API
        self.update_state(
            state="PROGRESS",
            meta={"status": "Конвертируем HTML в PDF через Cloudmersive...", "progress": 50}
        )

        pdf_bytes = _convert_html_to_pdf_cloudmersive(html_content)

        # Загружаем в S3
        self.update_state(
            state="PROGRESS",
            meta={"status": "Загружаем PDF в хранилище...", "progress": 80},
        )

        def run_s3_upload():
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                from app.services.pdf_report_service import pdf_report_service

                # Создаем имя файла
                safe_name = (
                    candidate_name
                    if candidate_name and candidate_name != "Не указано"
                    else "candidate"
                )
                safe_name = "".join(
                    c for c in safe_name if c.isalnum() or c in (" ", "-", "_")
                ).strip()
                report_id = report_data.get("id")
                filename = f"interview_report_{safe_name}_{report_id}.pdf"

                return loop.run_until_complete(
                    pdf_report_service.upload_pdf_to_s3(pdf_bytes, filename)
                )
            finally:
                loop.close()

        pdf_url = run_s3_upload()

        # Обновляем отчет с URL PDF файла
        self.update_state(
            state="PROGRESS",
            meta={"status": "Сохраняем ссылку на отчет...", "progress": 90},
        )

        report_id = report_data.get("id")
        with get_sync_session() as session:
            report_repo = SyncInterviewReportRepository(session)
            report_repo.update_pdf_url(report_id, pdf_url)

        # Завершено успешно
        self.update_state(
            state="SUCCESS",
            meta={
                "status": "PDF отчет успешно сгенерирован",
                "progress": 100,
                "pdf_url": pdf_url,
                "file_size": len(pdf_bytes),
            },
        )

        return {
            "interview_report_id": report_id,
            "status": "completed",
            "pdf_url": pdf_url,
            "file_size": len(pdf_bytes),
        }

    except Exception as e:
        # В случае ошибки
        self.update_state(
            state="FAILURE",
            meta={
                "status": f"Ошибка при генерации PDF: {str(e)}",
                "progress": 0,
                "error": str(e),
            },
        )

        raise Exception(f"Ошибка при генерации PDF: {str(e)}")


def _convert_html_to_pdf_cloudmersive(html_content: str) -> bytes:
    """
    Конвертирует HTML в PDF используя Cloudmersive API через HTTP requests

    Args:
        html_content: HTML содержимое для конвертации

    Returns:
        bytes: PDF файл в виде байтов
    """
    import requests
    from app.core.config import settings

    # Проверяем наличие API ключа
    if not hasattr(settings, 'cloudmersive_api_key') or not settings.cloudmersive_api_key:
        raise Exception("Cloudmersive API ключ не настроен в settings.cloudmersive_api_key")

    # Проверяем HTML на корректность
    if not html_content or len(html_content.strip()) == 0:
        raise Exception("HTML содержимое пустое")

    try:
        print(f"[Cloudmersive] Starting HTML to PDF conversion via HTTP...")
        print(f"[Cloudmersive] HTML length: {len(html_content)} characters")

        # Отправляем HTTP запрос с HTML как raw данные - правильный эндпоинт
        url = "https://api.cloudmersive.com/convert/html/to/pdf"
        headers = {
            'Apikey': settings.cloudmersive_api_key,
            'Content-Type': 'text/html'
        }

        print(f"[Cloudmersive] Sending HTTP request to {url}")
        print(f"[Cloudmersive] Sending HTML as raw data...")

        response = requests.post(
            url,
            headers=headers,
            data=html_content.encode('utf-8'),
            timeout=60
        )

        print(f"[Cloudmersive] Response status: {response.status_code}")

        if response.status_code == 200:
            pdf_bytes = response.content
            print(f"[Cloudmersive] Conversion successful, PDF size: {len(pdf_bytes)} bytes")
            return pdf_bytes
        else:
            print(f"[Cloudmersive] Error response: {response.text}")
            raise Exception(f"Cloudmersive API returned {response.status_code}: {response.text}")

    except requests.exceptions.RequestException as e:
        print(f"[Cloudmersive] HTTP Error: {e}")
        raise Exception(f"Ошибка HTTP запроса к Cloudmersive: {str(e)}")
    except Exception as e:
        print(f"[Cloudmersive] Unexpected error: {e}")
        raise Exception(f"Неожиданная ошибка при генерации PDF: {str(e)}")
