# #region DictionaryBatchFilter [C:3] [TYPE Class] [SEMANTICS dictionary, filter, batch, matching]
# @BRIEF Scan batch texts for case-insensitive, word-boundary-aware matches against dictionaries.
# @RELATION DEPENDS_ON -> [DictionaryEntry]
# @RELATION DEPENDS_ON -> [TranslationJobDictionary]
# @RELATION DEPENDS_ON -> [TerminologyDictionary]
# @RELATION DEPENDS_ON -> [ContextAwarePromptBuilder]

import re
from typing import Any

from sqlalchemy.orm import Session

from ...core.logger import belief_scope, logger
from ...models.translate import (
    DictionaryEntry,
    TerminologyDictionary,
    TranslationJobDictionary,
)
from .prompt_builder import ContextAwarePromptBuilder


class DictionaryBatchFilter:
    """Filter batch texts against dictionaries with word-boundary-aware matching."""

    # region filter_for_batch [TYPE Function]
    # @PURPOSE: Scan batch texts for matches against all dictionaries attached to a job.
    @staticmethod
    def filter_for_batch(
        db: Session, source_texts: list[str], job_id: str,
        source_language: str | None = None,
        target_language: str | None = None,
        row_context: dict | None = None,
    ) -> list[dict[str, Any]]:
        with belief_scope("DictionaryBatchFilter.filter_for_batch"):
            job_dict_links = (
                db.query(TranslationJobDictionary)
                .filter(TranslationJobDictionary.job_id == job_id)
                .all()
            )
            if not job_dict_links:
                logger.reason("No dictionaries attached to job", {"job_id": job_id})
                return []

            dict_ids = [jd.dictionary_id for jd in job_dict_links]
            dictionaries = (
                db.query(TerminologyDictionary)
                .filter(TerminologyDictionary.id.in_(dict_ids), TerminologyDictionary.is_active == True)
                .all()
            )
            if not dictionaries:
                return []

            dict_map = {d.id: d.name for d in dictionaries}
            all_entries = (
                db.query(DictionaryEntry)
                .filter(DictionaryEntry.dictionary_id.in_(dict_ids))
                .order_by(DictionaryEntry.source_term.asc())
                .all()
            )
            if not all_entries:
                return []

            matched: list[dict[str, Any]] = []
            seen_source_match: set = set()

            for text_idx, source_text in enumerate(source_texts):
                if not source_text:
                    continue
                text_lower = source_text.lower()

                for entry in all_entries:
                    norm = entry.source_term_normalized
                    if not norm:
                        continue

                    if source_language is not None:
                        src = source_language.strip().lower()
                        entry_src = entry.source_language.strip().lower()
                        if entry_src != src and entry_src != "und":
                            continue
                    if target_language is not None:
                        tgt = target_language.strip().lower()
                        entry_tgt = entry.target_language.strip().lower()
                        if entry_tgt != tgt:
                            continue

                    escaped = re.escape(norm)
                    pattern = re.compile(r"\b" + escaped + r"\b", re.IGNORECASE)

                    if pattern.search(text_lower):
                        match_key = (text_idx, entry.id)
                        if match_key not in seen_source_match:
                            seen_source_match.add(match_key)

                            priority_match = False
                            if row_context and entry.has_context and entry.context_data:
                                similarity = ContextAwarePromptBuilder.compute_context_similarity(
                                    entry.context_data, row_context
                                )
                                priority_match = similarity >= 0.5

                            matched.append({
                                "text_index": text_idx,
                                "source_text": source_text,
                                "entry_id": entry.id,
                                "source_term": entry.source_term,
                                "source_term_normalized": entry.source_term_normalized,
                                "target_term": entry.target_term,
                                "dictionary_id": entry.dictionary_id,
                                "dictionary_name": dict_map.get(entry.dictionary_id, ""),
                                "context_notes": entry.context_notes,
                                "context_data": entry.context_data,
                                "has_context": entry.has_context,
                                "context_source": entry.context_source,
                                "usage_notes": entry.usage_notes,
                                "source_language": entry.source_language,
                                "target_language": entry.target_language,
                                "priority_match": priority_match,
                            })

            dict_priority = {d_id: idx for idx, d_id in enumerate(dict_ids)}
            matched.sort(key=lambda m: (dict_priority.get(m["dictionary_id"], 999), m["text_index"]))

            logger.reflect("Batch filter match complete", {
                "job_id": job_id, "source_texts": len(source_texts),
                "matches": len(matched), "dictionaries_used": len(dictionaries),
            })
            return matched
    # endregion filter_for_batch

# #endregion DictionaryBatchFilter
