from __future__ import annotations

import json
import re
from typing import Any


class JsonFenceCleaner:
    def clean(self, text: str) -> str:
        cleaned = text.strip()
        if not cleaned:
            return cleaned
        if cleaned.startswith("```"):
            cleaned = re.sub(r"^```[a-zA-Z]*\n?", "", cleaned)
            cleaned = re.sub(r"```$", "", cleaned).strip()
        return cleaned


class SentenceArrayRepairer:
    def repair(self, 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 match.group(1) + "},{\"intent_type\""

        return pattern.sub(_insert_missing_brace, text)


class JsonObjectExtractor:
    def extract(self, text: str) -> Any:
        decoder = json.JSONDecoder()
        index = 0
        length = len(text)
        objects = []

        while index < length:
            next_obj = text.find("{", index)
            next_arr = text.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(text[index:])
            except json.JSONDecodeError:
                break

            objects.append(obj)
            index += end

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

        if not objects:
            return None
        if len(objects) == 1:
            return objects[0]
        return objects


class TransformedSentenceParser:
    def __init__(
        self,
        fence_cleaner: JsonFenceCleaner | None = None,
        repairer: SentenceArrayRepairer | None = None,
        extractor: JsonObjectExtractor | None = None,
    ) -> None:
        self._fence_cleaner = fence_cleaner or JsonFenceCleaner()
        self._repairer = repairer or SentenceArrayRepairer()
        self._extractor = extractor or JsonObjectExtractor()

    def parse(self, text: str) -> Any:
        cleaned = self._fence_cleaner.clean(text)
        if not cleaned:
            raise json.JSONDecodeError("Empty JSON", cleaned, 0)

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

        extracted = self._extractor.extract(cleaned)
        if extracted is not None:
            return extracted

        return json.loads(cleaned)

    def format_for_log(self, text: str) -> str:
        if text is None:
            return "(none)"
        cleaned = text.strip()
        if not cleaned:
            return "(empty)"
        try:
            parsed = self.parse(cleaned)
            return json.dumps(parsed, indent=2, ensure_ascii=False)
        except json.JSONDecodeError:
            return cleaned
