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


def get_sentence_transformer():
    return transform_sentence_with_llm


def _normalize_to_sentences_list(data: dict) -> dict:
    if "sentences" in data:
        return data
    if "relationships" in data:
        return {"sentences": data["relationships"]}
    if "intent" in data or "intent_type" in data:
        return {"sentences": [data]}
    return data


def _safe_json_loads(text: str):
    cleaned = text.strip()
    if not cleaned:
        raise json.JSONDecodeError("Empty JSON", cleaned, 0)

    if cleaned.startswith("```"):
        cleaned = re.sub(r"^```[a-zA-Z]*\n?", "", cleaned)
        cleaned = re.sub(r"```$", "", cleaned).strip()

    try:
        return json.loads(cleaned)
    except json.JSONDecodeError:
        repaired = _repair_sentence_array_json(cleaned)
        if repaired != cleaned:
            try:
                return json.loads(repaired)
            except json.JSONDecodeError:
                pass

    decoder = json.JSONDecoder()
    index = 0
    length = len(cleaned)
    objects = []

    while index < length:
        next_obj = cleaned.find("{", index)
        next_arr = cleaned.find("[", index)
        if next_obj == -1 and next_arr == -1:
            break
        if next_obj == -1 or (next_arr != -1 and next_arr < next_obj):
            index = next_arr
        else:
            index = next_obj

        try:
            obj, end = decoder.raw_decode(cleaned[index:])
        except json.JSONDecodeError:
            break

        objects.append(obj)
        index += end

        while index < length and cleaned[index] in " \t\r\n,":
            index += 1

    if objects:
        if len(objects) == 1:
            return objects[0]
        if all(isinstance(item, dict) for item in objects):
            return {"sentences": objects}
        return objects

    return json.loads(cleaned)


def _repair_sentence_array_json(text: str) -> str:
    if '"sentences"' not in text:
        return text
    pattern = re.compile(r'(\]\}\}|\}\})\s*,\s*\{"intent_type"')

    def _insert_missing_brace(match: re.Match) -> str:
        return f"{match.group(1)}}},{{\"intent_type\""

    repaired = pattern.sub(_insert_missing_brace, text)
    return repaired


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 _format_json_for_log(value: str) -> str:
    if value is None:
        return "(none)"
    text = value.strip()
    if not text:
        return "(empty)"
    try:
        parsed = _safe_json_loads(text)
        return json.dumps(parsed, indent=2, ensure_ascii=False)
    except json.JSONDecodeError:
        return text


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

    if is_json_format(text):
        app_logger.info(f"[SentenceTransformer] Skipping LLM - already JSON format")
        try:
            data = json.loads(text.strip())
            normalized = _normalize_to_sentences_list(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 = previous_messages_stack.get_history_pairs()
        history = []
        for role, content in history_pairs:
            if not content or not content.strip():
                continue
            label = "User" if role == "user" else "Agent"
            history.append(f"{label}: {content.strip()}")
        instruction = _load_sentence_transformer_prompt()
        if hasattr(gateway, "set_system_prompt"):
            gateway.set_system_prompt(instruction)

        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{_format_json_for_log(transformed)}\n"
        )
        try:
            data = _safe_json_loads(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{_format_json_for_log(retry_transformed)}\n"
            )
            try:
                data = _safe_json_loads(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_to_sentences_list(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
