# #region BulkFindReplaceService [C:3] [TYPE Class] [SEMANTICS translate, bulk, find, replace, regex]
# @BRIEF Service for bulk find-and-replace operations on translated values.
# @RELATION DEPENDS_ON -> [TranslationLanguage]
# @RELATION DEPENDS_ON -> [TranslationRecord]
# @RELATION DEPENDS_ON -> [DictionaryEntry]

import re
from typing import Any

from sqlalchemy.orm import Session

from ...core.logger import belief_scope, logger
from ...models.translate import DictionaryEntry, TranslationLanguage, TranslationRecord
from .service_inline_correction import InlineCorrectionService


class BulkFindReplaceService:
    """Handle bulk find-and-replace on TranslationLanguage entries."""

    MAX_PATTERN_LENGTH = 500

    @staticmethod
    def _compile_pattern(pattern: str, is_regex: bool) -> re.Pattern:
        """Compile a search pattern (regex or plain text)."""
        if len(pattern) > BulkFindReplaceService.MAX_PATTERN_LENGTH:
            raise ValueError(f"Pattern too long (max {BulkFindReplaceService.MAX_PATTERN_LENGTH} characters)")
        try:
            return re.compile(pattern) if is_regex else re.compile(re.escape(pattern))
        except re.error as e:
            raise ValueError(f"Invalid regex pattern: {e}")

    @staticmethod
    def _find_matching_entries(
        db: Session,
        run_id: str,
        pattern: str,
        is_regex: bool,
        target_language: str,
    ) -> list[Any]:
        """Find all TranslationLanguage entries matching the pattern."""
        entries = (
            db.query(TranslationLanguage)
            .join(TranslationRecord, TranslationRecord.id == TranslationLanguage.record_id)
            .filter(
                TranslationRecord.run_id == run_id,
                TranslationLanguage.language_code == target_language,
            )
            .all()
        )
        return entries

    @staticmethod
    def preview(
        db: Session,
        run_id: str,
        pattern: str,
        is_regex: bool,
        replacement_text: str,
        target_language: str,
    ) -> list[dict]:
        """Scan TranslationLanguage entries and return matching items without applying changes."""
        compiled = BulkFindReplaceService._compile_pattern(pattern, is_regex)
        entries = BulkFindReplaceService._find_matching_entries(
            db, run_id, pattern, is_regex, target_language
        )

        preview_items = []
        for entry in entries:
            current_value = entry.final_value or entry.translated_value or ""
            if compiled.search(current_value):
                record = (
                    db.query(TranslationRecord)
                    .filter(TranslationRecord.id == entry.record_id)
                    .first()
                )
                source_term = record.source_sql if record else ""
                new_value = compiled.sub(replacement_text, current_value)
                if new_value != current_value:
                    preview_items.append({
                        "record_id": entry.record_id,
                        "language_code": entry.language_code,
                        "source_term": source_term or "",
                        "current_value": current_value,
                        "new_value": new_value,
                        "source_language_detected": entry.source_language_detected,
                    })
        return preview_items

    @staticmethod
    def apply(
        db: Session,
        run_id: str,
        pattern: str,
        is_regex: bool,
        replacement_text: str,
        target_language: str,
        submit_to_dictionary: bool = False,
        dictionary_id: str | None = None,
        usage_notes: str | None = None,
        current_user: str | None = None,
        submit_to_dictionary_with_context: bool = False,
    ) -> dict:
        """Apply find-and-replace to matching TranslationLanguage entries."""
        with belief_scope("BulkFindReplaceService.apply"):
            compiled = BulkFindReplaceService._compile_pattern(pattern, is_regex)
            entries = BulkFindReplaceService._find_matching_entries(
                db, run_id, pattern, is_regex, target_language
            )

            preview_items = []
            rows_affected = 0
            corrections_submitted = 0
            seen_term_replacements: dict[str, str] = {}

            for entry in entries:
                current_value = entry.final_value or entry.translated_value or ""
                if compiled.search(current_value):
                    new_value = compiled.sub(replacement_text, current_value)
                    if new_value != current_value:
                        entry.final_value = new_value
                        entry.user_edit = new_value
                        if entry.status in ("pending", "translated"):
                            entry.status = "edited"
                        rows_affected += 1

                        record = (
                            db.query(TranslationRecord)
                            .filter(TranslationRecord.id == entry.record_id)
                            .first()
                        )
                        source_term = record.source_sql if record else ""

                        if submit_to_dictionary and dictionary_id:
                            try:
                                dict_result = InlineCorrectionService.submit_correction_to_dict(
                                    db=db, record_id=entry.record_id,
                                    language_code=entry.language_code,
                                    dictionary_id=dictionary_id,
                                    corrected_value=new_value,
                                    current_user=current_user,
                                )
                                if dict_result.get("action") in ("created", "updated"):
                                    corrections_submitted += 1

                                if submit_to_dictionary_with_context and record:
                                    term_key = f"{source_term}::{new_value}"
                                    if term_key not in seen_term_replacements:
                                        seen_term_replacements[term_key] = entry.record_id
                                        context_data = {}
                                        if record.source_data:
                                            context_data["source_data"] = record.source_data
                                        if record.source_object_type:
                                            context_data["source_object_type"] = record.source_object_type
                                        if record.source_object_name:
                                            context_data["source_object_name"] = record.source_object_name
                                        if record.source_object_id:
                                            context_data["source_object_id"] = record.source_object_id
                                        if dict_result.get("action") == "created" and dict_result.get("entry_id"):
                                            d_entry = (
                                                db.query(DictionaryEntry)
                                                .filter(DictionaryEntry.id == dict_result["entry_id"])
                                                .first()
                                            )
                                            if d_entry:
                                                d_entry.context_data = context_data if context_data else None
                                                d_entry.has_context = bool(context_data)
                                                d_entry.context_source = "bulk"
                            except Exception as e:
                                logger.explore("Bulk replace: dictionary submission failed", extra={"record_id": entry.record_id, "error": str(e)})

                        preview_items.append({
                            "record_id": entry.record_id,
                            "language_code": entry.language_code,
                            "source_term": source_term or "",
                            "current_value": current_value,
                            "new_value": new_value,
                            "source_language_detected": entry.source_language_detected,
                        })

            db.commit()

            result = {
                "rows_affected": rows_affected,
                "corrections_submitted": corrections_submitted,
                "preview": preview_items,
            }
            logger.reason("Bulk replace completed", result)
            return result
# #endregion BulkFindReplaceService
