"""
sentient_bot.py
===============

ממשק שפה טבעית לבסיס נתונים על גבי אתנה.
"""

import json
import logging
import uuid
from typing import Any, Dict, Optional
from .athena import אתנה
from .fernhub import פרנהאב

logger = logging.getLogger(__name__)


class בוט_תבוני(אתנה):
    """
    בוט בסיס נתונים תבוני יחיד.
    """

    def __init__(
        self,
        מפתח_אבטחה: str,
        מפתח_בוט: str,
        נתיב_תצורת_בוטים: str = "bots.json",
        מופע: Optional[str] = None,
        פרנהאב_: Optional[פרנהאב] = None,
        **kwargs,
    ):
        if "security_key" in kwargs:
            מפתח_אבטחה = kwargs["security_key"]
        if "bot_key" in kwargs:
            מפתח_בוט = kwargs["bot_key"]
        if "bots_config_path" in kwargs:
            נתיב_תצורת_בוטים = kwargs["bots_config_path"]
        if "instance" in kwargs:
            מופע = kwargs["instance"]
        if "fernhub" in kwargs:
            פרנהאב_ = kwargs["fernhub"]

        super().__init__(מפתח_אבטחה, מופע or מפתח_בוט)

        self.פרנהאב = פרנהאב_
        self.fernhub = self.פרנהאב  # תאימות אנגלית
        self.מפתח_בוט = מפתח_בוט
        self.bot_key = self.מפתח_בוט  # תאימות אנגלית

        with open(f"config/{נתיב_תצורת_בוטים}", "r", encoding="utf-8") as קובץ:
            בוטים = json.load(קובץ)

        if מפתח_בוט not in בוטים:
            raise ValueError(f"Bot '{מפתח_בוט}' not found in bots.json")

        self.בוט = בוטים[מפתח_בוט]
        self.bot = self.בוט
        self.סכמה = self.בוט["attribute_structure"]["fields"]
        self.schema = self.סכמה
        self.יישות = self.בוט["attribute_structure"]["name"]
        self.entity = self.יישות
        self.הנחיית_מערכת = self.בוט["system_prompt"]
        self.system_prompt = self.הנחיית_מערכת
        self.מטמון_שאילתות = {}
        self.query_cache = self.מטמון_שאילתות

        if self.פרנהאב:
            self._הגדר_הרשמות_פרנהאב()

    def _הגדר_הרשמות_פרנהאב(self):
        """הגדרת הרשמות לאירועים לפי סוג הבוט."""
        if self.מפתח_בוט == "bid":
            self.פרנהאב.הירשם("job_created", self._טפל_נוצרה_עבודה)
        elif self.מפתח_בוט == "job":
            self.פרנהאב.הירשם("bid_placed", self._טפל_הוגשה_הצעה)
            self.פרנהאב.הירשם("bidding_ended", self._טפל_נסתיימה_הצעה)

    def _טפל_נוצרה_עבודה(self, נתוני_אירוע: Dict[str, Any]):
        """טיפול באירוע יצירת עבודה (לבוט הצעות)."""
        if self.מפתח_בוט == "bid":
            מזהה_עבודה = נתוני_אירוע.get("job_id")
            if מזהה_עבודה:
                logger.info(
                    "[%s] Received job_created event for job: %s", self.מפתח_בוט, מזהה_עבודה
                )

    def _טפל_הוגשה_הצעה(self, נתוני_אירוע: Dict[str, Any]):
        """טיפול באירוע הצעה (לבוט עבודות)."""
        if self.מפתח_בוט == "job":
            מזהה_עבודה = נתוני_אירוע.get("job_id")
            סכום_הצעה = נתוני_אירוע.get("bid_amount")
            logger.info(
                "[%s] Received bid_placed event for job %s: %s Quanta",
                self.מפתח_בוט,
                מזהה_עבודה,
                סכום_הצעה,
            )

    def _טפל_נסתיימה_הצעה(self, נתוני_אירוע: Dict[str, Any]):
        """טיפול בסיום הצעות (לבוט עבודות)."""
        if self.מפתח_בוט == "job":
            מזהה_עבודה = נתוני_אירוע.get("job_id")
            זוכה = נתוני_אירוע.get("winner")
            logger.info(
                "[%s] Received bidding_ended event for job %s, winner: %s",
                self.מפתח_בוט,
                מזהה_עבודה,
                זוכה,
            )

    def פרסם_אירוע(self, סוג_אירוע: str, נתוני_אירוע: Dict[str, Any]):
        """פרסום אירוע ל-FernHub."""
        if self.פרנהאב:
            נתוני_אירוע["source_bot"] = self.מפתח_בוט
            נתוני_אירוע["source_instance"] = self.מופע
            self.פרנהאב.פרסם(סוג_אירוע, נתוני_אירוע)
            logger.info("[%s] Published event: %s", self.מפתח_בוט, סוג_אירוע)
        else:
            logger.warning(
                "[%s] No FernHub available to publish event: %s",
                self.מפתח_בוט,
                סוג_אירוע,
            )

    # =====================================================
    # ממשק ציבורי
    # =====================================================

    def שאילתה(self, הנחיה: str) -> Any:
        """
        נקודת כניסה ראשית.
        """
        if הנחיה in self.מטמון_שאילתות:
            return self.מטמון_שאילתות[הנחיה]
        תוצאה = self._פרש(הנחיה)
        if isinstance(תוצאה, list):
            self.מטמון_שאילתות[הנחיה] = תוצאה
        return תוצאה

    # =====================================================
    # פרשנות (LLM → מבנה)
    # =====================================================

    def _פרש(self, הנחיה: str) -> Any:
        """
        פרשנות ההנחיה וביצוע פעולות.
        """
        טקסט = הנחיה.strip().lower()
        if טקסט == "list all jobs":
            return self._שלוף({})

        if טקסט.startswith("search "):
            מילת_מפתח = הנחיה[7:].strip()
            return self._חפש(מילת_מפתח)

        מזהה_עבודה = f"job_{str(uuid.uuid4())[:8]}"
        הנחיית_ניתוח = f"""
From this job description: "{הנחיה}"

Extract and generate the following job data as JSON:

- job_id: "{מזהה_עבודה}"
- client_description: "{הנחיה}"
- description: a detailed description based on the input
- category: choose from: Brand & Visual Identity Design, Marketing & Advertising Design, UI/UX & Digital Design, Publication & Editorial Design, Packaging Design, Motion Graphics Design, Environmental Graphic Design, Illustration & Lettering
- required_level: integer 1-4 based on complexity (1=beginner, 4=expert)
- budget: estimated budget as string, e.g., "$500"
- deadline: estimated deadline as ISO string, e.g., "2023-12-15"
- status: "open"
- assigned_designer: null
- client_contact: "client@example.com"
- created_at: current timestamp as ISO string, e.g., "2023-11-20T10:00:00Z"
- updated_at: same as created_at
- offers_created: 0
- activity_count: 0
- progress: 0

Return only the JSON object, no extra text.
"""

        מחרוזת_מפורשת = self.סינתיה.שיחה(
            הנחיית_ניתוח,
            מערכת="You are a data extractor. Return only valid JSON.",
        )

        מחרוזת_מפורשת = מחרוזת_מפורשת.strip()
        if מחרוזת_מפורשת.startswith("```json"):
            מחרוזת_מפורשת = מחרוזת_מפורשת[7:]
        if מחרוזת_מפורשת.startswith("```"):
            מחרוזת_מפורשת = מחרוזת_מפורשת[3:]
        if מחרוזת_מפורשת.endswith("```"):
            מחרוזת_מפורשת = מחרוזת_מפורשת[:-3]
        מחרוזת_מפורשת = מחרוזת_מפורשת.strip()

        נתוני_עבודה = json.loads(מחרוזת_מפורשת)
        return self._צור(נתוני_עבודה)

    # =====================================================
    # ביצוע (מבנה → זיכרון)
    # =====================================================

    def _בצע(self, תכנית: Dict[str, Any]) -> Any:
        פעולה = תכנית.get("operation")
        מסננים = תכנית.get("filters", {})
        נתונים = תכנית.get("data", {})

        if פעולה == "create":
            return self._צור(נתונים)

        if פעולה == "retrieve":
            return self._שלוף(מסננים)

        if פעולה == "update":
            return self._עדכן(מסננים, נתונים)

        raise ValueError(f"Unknown operation: {פעולה}")

    # =====================================================
    # פעולות CRUD
    # =====================================================

    def _צור(self, נתונים: Dict[str, Any]) -> Dict[str, Any]:
        רשומה = {}

        for שדה, ערך in נתונים.items():
            אינדקס = self.מקה.עובדה(ערך, סמל=f"{self.יישות}_{שדה}_{uuid.uuid4().hex[:8]}")
            רשומה[שדה] = אינדקס

        מזהה_רשומה = str(uuid.uuid4())
        סמל = f"{self.יישות}_record_{מזהה_רשומה}"
        self.מקה.עובדה(רשומה, סמל=סמל)

        if self.פרנהאב and self.מפתח_בוט == "job":
            self.פרסם_אירוע("job_created", {
                "job_id": נתונים.get("job_id"),
                "data": נתונים,
                "record_id": מזהה_רשומה
            })

        return {
            "status": "created",
            "record_id": מזהה_רשומה,
            "data": נתונים,
        }

    def _שלוף(self, מסננים: Dict[str, Any]) -> Any:
        """
        שליפת רשומות מהאחסון המתמיד.
        """
        התאמות = []

        for סמל, אינדקס in self.מקה.סמלים.items():
            if "_record_" not in סמל or not סמל.startswith(self.יישות):
                continue

            try:
                רשומה = self.מקה.היזכר(אינדקס)
                if not isinstance(רשומה, dict):
                    continue

                if self._תואם_מסננים(רשומה, מסננים):
                    התאמות.append(self._החזר_רשומה(רשומה))
            except Exception:
                continue

        try:
            גודל_זיכרון = self.מקה.ליבה.זיכרון.גודל()
            for אינדקס in range(1, גודל_זיכרון):
                try:
                    ערך = self.מקה.ליבה.זיכרון.קרא(אינדקס)

                    if isinstance(ערך, dict) and not ערך.get("__symbols__"):
                        if all(isinstance(v, int) for v in ערך.values()):
                            if self._תואם_מסננים(ערך, מסננים):
                                רשומה_מלאה = self._החזר_רשומה(ערך)
                                if not isinstance(רשומה_מלאה.get("job_id"), str):
                                    continue
                                if רשומה_מלאה.get("job_id") == "000":
                                    continue
                                if רשומה_מלאה not in התאמות:
                                    התאמות.append(רשומה_מלאה)
                except Exception:
                    continue
        except Exception:
            logger.exception("[%s] Memory scan error", self.מפתח_בוט)

        return התאמות

    def _חפש(self, מילת_מפתח: str) -> Any:
        """
        חיפוש רשומות לפי מילת מפתח.
        """
        כל_העבודות = self._שלוף({})
        התאמות = []
        מילת_מפתח_קטנה = מילת_מפתח.lower()
        for עבודה in כל_העבודות:
            if מילת_מפתח_קטנה in עבודה.get("client_description", "").lower() or מילת_מפתח_קטנה in עבודה.get("description", "").lower():
                התאמות.append(עבודה)
        return התאמות

    def _תואם_מסננים(self, רשומה: Dict[str, int], מסננים: Dict[str, Any]) -> bool:
        """בדיקה האם רשומה תואמת מסננים."""
        for שדה, ערך in מסננים.items():
            אינדקס = רשומה.get(שדה)
            if אינדקס is None:
                return False
            try:
                if self.מקה.היזכר(אינדקס) != ערך:
                    return False
            except Exception:
                return False
        return True

    def _החזר_רשומה(self, רשומה: Dict[str, int]) -> Dict[str, Any]:
        """המרת רשומת אינדקסים לערכים."""
        רשומה_מלאה = {}
        for מפתח, אינדקס in רשומה.items():
            try:
                ערך = self.מקה.היזכר(אינדקס)
                רשומה_מלאה[מפתח] = ערך
            except Exception:
                continue
        return רשומה_מלאה

    def _עדכן(self, מסננים: Dict[str, Any], נתונים: Dict[str, Any]) -> Dict[str, Any]:
        """עדכון רשומות תואמות."""
        עודכן = 0

        for סמל, אינדקס in list(self.מקה.סמלים.items()):
            if "_record_" not in סמל or not סמל.startswith(self.יישות):
                continue

            try:
                רשומה = self.מקה.היזכר(אינדקס)
                if not isinstance(רשומה, dict):
                    continue

                if self._תואם_מסננים(רשומה, מסננים):
                    for שדה, ערך in נתונים.items():
                        סמל_שדה = f"{self.יישות}_{שדה}_{uuid.uuid4().hex[:8]}"
                        אינדקס_חדש = self.מקה.עובדה(ערך, סמל=סמל_שדה)
                        רשומה[שדה] = אינדקס_חדש

                    self.מקה.ליבה.זיכרון.כתוב(אינדקס, רשומה)
                    עודכן += 1
            except Exception:
                continue

        try:
            גודל_זיכרון = self.מקה.ליבה.זיכרון.גודל()
            for אינדקס in range(1, גודל_זיכרון):
                try:
                    ערך = self.מקה.ליבה.זיכרון.קרא(אינדקס)

                    if isinstance(ערך, dict) and not ערך.get("__symbols__"):
                        if all(isinstance(v, int) for v in ערך.values()):
                            if self._תואם_מסננים(ערך, מסננים):
                                for שדה, ערך_חדש in נתונים.items():
                                    סמל_שדה = f"{self.יישות}_{שדה}_{uuid.uuid4().hex[:8]}"
                                    אינדקס_חדש = self.מקה.עובדה(ערך_חדש, סמל=סמל_שדה)
                                    ערך[שדה] = אינדקס_חדש

                                self.מקה.ליבה.זיכרון.כתוב(אינדקס, ערך)
                                עודכן += 1
                except Exception:
                    continue
        except Exception:
            logger.exception("[%s] Update memory scan error", self.מפתח_בוט)

        return {"status": "updated", "count": עודכן}

    # --- תאימות אנגלית ---
    publish_event = פרסם_אירוע
    query = שאילתה
    _setup_fernhub_subscriptions = _הגדר_הרשמות_פרנהאב
    _handle_job_created = _טפל_נוצרה_עבודה
    _handle_bid_placed = _טפל_הוגשה_הצעה
    _handle_bidding_ended = _טפל_נסתיימה_הצעה
    _interpret = _פרש
    _execute = _בצע
    _create = _צור
    _retrieve = _שלוף
    _search = _חפש
    _matches_filters = _תואם_מסננים
    _hydrate_record = _החזר_רשומה
    _update = _עדכן


# תאימות שמות
SentientBot = בוט_תבוני
