import re
import json
from typing import Dict, Any, Optional, Sequence


def estimate_tokens(text: str) -> int:
    words = len(text.split())
    chars = len(text)
    return max(words, chars // 4)


def normalize_text_for_translation(text: str) -> str:
    """Normalize text by removing special characters that can cause JSON parsing issues."""
    if not text:
        return text
    
    # Replace Unicode quotation marks with ASCII quotes
    quote_replacements = {
        '"': '"',  # U+201C Left double quotation mark
        '"': '"',  # U+201D Right double quotation mark
        ''': "'",  # U+2018 Left single quotation mark
        ''': "'",  # U+2019 Right single quotation mark
        '‚': "'",  # U+201A Single low-9 quotation mark
        '„': '"',  # U+201E Double low-9 quotation mark
        '‹': "'",  # U+2039 Single left-pointing angle quotation mark
        '›': "'",  # U+203A Single right-pointing angle quotation mark
        '«': '"',  # U+00AB Left-pointing double angle quotation mark
        '»': '"',  # U+00BB Right-pointing double angle quotation mark
    }
    
    normalized = text
    for unicode_char, ascii_char in quote_replacements.items():
        normalized = normalized.replace(unicode_char, ascii_char)
    
    # Remove or replace other problematic characters
    # Remove control characters and non-printable characters
    normalized = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', normalized)
    
    # Replace problematic punctuation with safe alternatives
    replacements = {
        '\u2013': '-',  # En dash
        '\u2014': '-',  # Em dash
        '\u2026': '...',  # Ellipsis
        '\u00a0': ' ',   # Non-breaking space
        '\u202f': ' ',   # Narrow non-breaking space
        '\u2009': ' ',   # Thin space
        '\u200b': '',    # Zero-width space
        '\u200c': '',    # Zero-width non-joiner
        '\u200d': '',    # Zero-width joiner
        '\ufeff': '',    # Byte order mark
    }
    
    for char, replacement in replacements.items():
        normalized = normalized.replace(char, replacement)
    
    # Clean up multiple spaces and trim
    normalized = re.sub(r'\s+', ' ', normalized).strip()
    
    return normalized


def ensure_all_language_blocks(text: str, languages: Sequence[str]) -> str:
    language_codes = tuple(languages)

    if not text:
        return " ".join(f"<!-- start {code} --><!-- end {code} -->" for code in language_codes)

    normalized = text.strip()

    for code in language_codes:
        start_tag = f"<!-- start {code} -->"
        end_tag = f"<!-- end {code} -->"
        if start_tag not in normalized or end_tag not in normalized:
            normalized = f"{normalized} {start_tag}{end_tag}".strip()

    return normalized


def detect_case_style(text: str) -> str:
    if not text:
        return "none"

    letters = [ch for ch in text if ch.isalpha() and ch.lower() != ch.upper()]
    if not letters:
        return "none"

    if all(ch.isupper() for ch in letters):
        return "upper"
    if all(ch.islower() for ch in letters):
        return "lower"
    return "mixed"


def apply_case_style(text: str, style: str) -> str:
    if not text or style not in ("upper", "lower"):
        return text
    return text.upper() if style == "upper" else text.lower()


def strip_markdown_code_fences(s: str) -> str:
    s = s.strip()
    code_block_start = re.compile(r"^```")
    code_block_end = re.compile(r"```$")
    
    if code_block_start.match(s):
        s = s.split("\n", 1)[1] if "\n" in s else ""
    if code_block_end.search(s):
        s = s.rsplit("\n", 1)[0]
    return s.strip()


def fix_json_strings(json_str: str) -> str:
    """Fix unescaped quotes and other issues within JSON string values."""
    import json as json_module
    
    # Try to parse as-is first
    try:
        json_module.loads(json_str)
        return json_str
    except json_module.JSONDecodeError:
        pass
    
    # Find and fix string values that contain unescaped quotes
    # Look for patterns like: "key": "value with "quotes" inside"
    def fix_string_value(match):
        key = match.group(1)
        value_with_quotes = match.group(2)
        
        # Escape quotes within the value, but preserve already escaped ones
        fixed_value = value_with_quotes.replace('\\"', '\x00ESCAPED_QUOTE\x00')  # Temporarily protect escaped quotes
        fixed_value = fixed_value.replace('"', '\\"')  # Escape unescaped quotes
        fixed_value = fixed_value.replace('\x00ESCAPED_QUOTE\x00', '\\"')  # Restore escaped quotes
        
        return f'"{key}": "{fixed_value}"'
    
    # Pattern to match key-value pairs with problematic string values
    # This handles the case where there are unescaped quotes in the value
    pattern = r'"([^"]+)":\s*"([^"]*"[^"]*)"(?=\s*[,}])'
    fixed = re.sub(pattern, fix_string_value, json_str)
    
    return fixed


def extract_json_block(s: str) -> str:
    s = strip_markdown_code_fences(s)
    json_capture = re.compile(r"\{.*\}", re.DOTALL)
    m = json_capture.search(s)
    if m:
        s = m.group(0)
    
    # Fix string values with unescaped quotes
    s = fix_json_strings(s)
    
    # Remove trailing commas
    s = re.sub(r",\s*([}\]])", r"\1", s)
    return s


def validate_and_parse_json(raw_json: str) -> Dict[str, Any]:
    try:
        cleaned_json = extract_json_block(raw_json)
        return json.loads(cleaned_json)
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON response: {e}")


def calculate_cost(input_tokens: int, output_tokens: int, model: str) -> float:
    # Token pricing per 1K tokens
    TOKEN_PRICING = {
        "gpt-3.5-turbo": {"input": 0.0015, "output": 0.002},
        "gpt-4o-mini": {"input": 0.00015, "output": 0.0006},
        "gpt-4o": {"input": 0.0025, "output": 0.01},
        "gpt-4": {"input": 0.03, "output": 0.06},
        "gpt-4-turbo": {"input": 0.01, "output": 0.03},
    }
    pricing = TOKEN_PRICING.get(model, TOKEN_PRICING["gpt-4o-mini"])
    input_cost = (input_tokens / 1000) * pricing["input"]
    output_cost = (output_tokens / 1000) * pricing["output"]
    return input_cost + output_cost


def extract_english_translation(text: str) -> Optional[str]:
    """Extract English translation from a text containing language blocks."""
    if not text:
        return None
    
    start_tag = "<!-- start en -->"
    end_tag = "<!-- end en -->"
    
    start_idx = text.find(start_tag)
    if start_idx == -1:
        return None
    
    start_idx += len(start_tag)
    end_idx = text.find(end_tag, start_idx)
    if end_idx == -1:
        return None
    
    english_text = text[start_idx:end_idx].strip()
    return english_text if english_text else None


def remove_english_translation(text: str) -> str:
    """Remove English translation block from text containing language blocks."""
    if not text:
        return text
    
    start_tag = "<!-- start en -->"
    end_tag = "<!-- end en -->"
    
    start_idx = text.find(start_tag)
    if start_idx == -1:
        return text
    
    end_idx = text.find(end_tag, start_idx)
    if end_idx == -1:
        return text
    
    # Remove the entire English block including tags
    end_idx += len(end_tag)
    result = text[:start_idx] + text[end_idx:]
    
    # Clean up extra spaces
    result = re.sub(r'\s+', ' ', result).strip()
    
    return result
