import json
import re
from pathlib import Path

from analogai.deepthink.infrastructure.gateways.llm.llm_factory import LlmGatewayFactory
from analogai.deepthink.infrastructure.gateways.llm.providers.sentence_transformer_examples import (
    get_enhanced_system_prompt,
)
from analogai.foundation.common.logging.app_logger import app_logger
from analogai.deepthink.presentation.preprocessing.previous_messages_stack import PreviousMessagesStack
from analogai.deepthink.presentation.parsers.json_sentence_parser import is_json_format
from analogai.deepthink.presentation.preprocessing.preprocessors.llm.transformed_sentence_normalizer import (
    normalize_transformed_sentence,
)
from analogai.deepthink.presentation.preprocessing.preprocessors.llm.transformed_sentence_parser import (
    TransformedSentenceParser,
)


def get_sentence_transformer():
    return transform_sentence_with_llm


def _load_sentence_transformer_prompt() -> str:
    prompts_dir = Path(__file__).resolve().parents[3] / "infrastructure" / "prompts"
    prompt_path = prompts_dir / "sentence_transformer_prompt.txt"
    if not prompt_path.exists():
        raise FileNotFoundError(f"Prompt file not found: {prompt_path}")
    base_prompt = prompt_path.read_text(encoding="utf-8").strip()
    return get_enhanced_system_prompt(base_prompt)


def transform_sentence_with_llm(text: str, previous_messages_stack: PreviousMessagesStack) -> str:
    if not text or not text.strip():
        return text

    parser = TransformedSentenceParser()

    if is_json_format(text):
        app_logger.info(f"[SentenceTransformer] Skipping LLM - already JSON format")
        try:
            data = parser.parse(text.strip())
            normalized = normalize_transformed_sentence(data)
            return json.dumps(normalized)
        except json.JSONDecodeError as e:
            app_logger.error(
                "[SentenceTransformer] Invalid JSON input provided. "
                f"reason={e} input_text={text}"
            )
            return text

    try:
        gateway = LlmGatewayFactory.create_sentence_decomposer_and_transformer()

        sanitized_text = text.replace('"', "").strip()
        if sanitized_text.startswith(("- ", "* ", "• ")):
            sanitized_text = sanitized_text[2:].strip()
        sanitized_text = re.sub(r"^\d+[\.)]\s+", "", sanitized_text)

        history_pairs = list(previous_messages_stack.get_history_pairs())
        if history_pairs:
            last_role, last_content = history_pairs[-1]
            if last_role == "user" and last_content and last_content.strip() == sanitized_text:
                history_pairs = history_pairs[:-1]
        sanitized_text = _resolve_pronouns_with_history(sanitized_text, history_pairs)
        history = []
        for role, content in history_pairs:
            if not content or not content.strip():
                continue
            label = "Q" if role == "user" else "A"
            history.append(f"{label}: {content.strip()}")
        instruction = _load_sentence_transformer_prompt()
        try:
            gateway.set_system_prompt(instruction)
        except AttributeError:
            pass

        if history:
            history_text = "\n".join(history)
            user_message = (
                f"Conversation history (for context only): {history_text}\n\n"
                f"Sentence: {sanitized_text}"
            )
        else:
            user_message = f"Sentence: {sanitized_text}"

        app_logger.info(
            "[SentenceTransformer] LLM call\n"
            f"Input:\n{user_message}\n"
        )
        transformed = gateway.generate_completion(user_message)
        app_logger.info(
            "[SentenceTransformer] LLM output\n"
            f"Output:\n{parser.format_for_log(transformed)}\n"
        )
        try:
            data = parser.parse(transformed)
        except json.JSONDecodeError as e:
            app_logger.warning(
                "[SentenceTransformer] Invalid JSON from LLM, retrying once. "
                f"reason={e} input_text={text}"
            )
            retry_transformed = gateway.generate_completion(user_message)
            app_logger.info(
                "[SentenceTransformer] LLM retry output\n"
                f"Output:\n{parser.format_for_log(retry_transformed)}\n"
            )
            try:
                data = parser.parse(retry_transformed)
                transformed = retry_transformed
            except json.JSONDecodeError as retry_error:
                app_logger.error(
                    "[SentenceTransformer] LLM returned invalid JSON. "
                    f"reason={retry_error} input_text={text} history={history} "
                    f"user_message={user_message} llm_output={retry_transformed}"
                )
                return text
        normalized = normalize_transformed_sentence(data)

        return json.dumps(normalized)
    except Exception as e:
        app_logger.error(
            "[SentenceTransformer] Error transforming sentence with LLM. "
            f"reason={e} input_text={text}"
        )
        return text


def _resolve_pronouns_with_history(sentence: str, history_pairs: list[tuple[str, str]]) -> str:
    if not sentence or not history_pairs:
        return sentence
    last_subject = _extract_last_subject(history_pairs)
    if not last_subject:
        return sentence
    replacements = {
        " he ": f" {last_subject} ",
        " she ": f" {last_subject} ",
        " they ": f" {last_subject} ",
        " him ": f" {last_subject} ",
        " her ": f" {last_subject} ",
    }
    padded = f" {sentence.strip()} "
    lowered = padded.lower()
    for token, replacement in replacements.items():
        if token in lowered:
            lowered = lowered.replace(token, replacement)
    return lowered.strip()


def _extract_last_subject(history_pairs: list[tuple[str, str]]) -> str | None:
    for role, content in reversed(history_pairs):
        if role != "user" or not content:
            continue
        text = content.strip()
        for sep in (" is ", " are "):
            if sep in text:
                return text.split(sep)[0].strip()
        return text.split()[0] if text else None
    return None
