import sys
import os
import ast
import json
import logging
from datetime import datetime
from typing import Dict, Any, Optional
from PyQt6.QtWidgets import (
    QMessageBox, QPushButton, QTableWidgetItem, QTableWidget
)
from PyQt6.QtGui import QFont, QTextCharFormat, QColor
from PyQt6.QtCore import Qt, QDate, QTimer
from core.db import BaseManager, DataManager
from core.config import format_number, logger

_CALC_BINARY_OPS = {
    ast.Add: lambda a, b: a + b,
    ast.Sub: lambda a, b: a - b,
    ast.Mult: lambda a, b: a * b,
    ast.Div: lambda a, b: a / b,
}

_CALC_UNARY_OPS = {
    ast.UAdd: lambda a: a,
    ast.USub: lambda a: -a,
}

def _safe_calculate_expression(expression: str) -> float:
    parsed = ast.parse(expression, mode="eval")

    def _eval_node(node):
        if isinstance(node, ast.Expression):
            return _eval_node(node.body)
        if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
            return node.value
        if isinstance(node, ast.Num):
            return node.n
        if isinstance(node, ast.BinOp) and type(node.op) in _CALC_BINARY_OPS:
            left = _eval_node(node.left)
            right = _eval_node(node.right)
            return _CALC_BINARY_OPS[type(node.op)](left, right)
        if isinstance(node, ast.UnaryOp) and type(node.op) in _CALC_UNARY_OPS:
            operand = _eval_node(node.operand)
            return _CALC_UNARY_OPS[type(node.op)](operand)
        raise ValueError("Desteklenmeyen ifade")

    return float(_eval_node(parsed))
class CalendarManager(BaseManager):
    """
    Ajanda notlarını yönetir.
    SQLite'a geçişte BaseManager liste döndürdüğü için,
    bu sınıf data property'sini ezerek (override) Sözlük (Dict) mantığıyla çalışır.
    """
    def __init__(self, data_manager: DataManager):
        super().__init__(data_manager, "agenda_notes", dict)
        self._cached_data: Dict[str, Dict[str, Any]] = self.data_manager.load_data("agenda_notes", default_value={})
    @property
    def data(self) -> Dict[str, Dict[str, Any]]:
        """BaseManager'ın liste döndüren yapısını eziyoruz. Sözlük döndürür."""
        return self._cached_data
    @data.setter
    def data(self, value):
        self._cached_data = value
    def get_note_data(self, date_str: str) -> Dict[str, Any]:
        """Tarihe göre not verisini döndürür."""
        note = self.data.get(date_str, {})
        if isinstance(note, str):
            return {"html": note, "is_reminder": False}
        return note
    def save_note(self, date_str: str, html_content: str, is_reminder: bool) -> None:
        """Notu kaydeder ve veritabanına yazar."""
        if html_content and html_content.strip() and not ("<body><p><br></p></body>" in html_content.replace(" ", "")):
            self.data[date_str] = {"html": html_content, "is_reminder": is_reminder}
        elif date_str in self.data:
            del self.data[date_str]
        self.save()
    def delete_note(self, date_str: str) -> None:
        if date_str in self.data:
            del self.data[date_str]
            self.save()
    def save(self) -> None:
        """Veriyi SQLite veritabanına kaydeder."""
        self.data_manager.save_data("agenda_notes", self.data)
class UtilityManager:
    """
    Yardımcı Araçlar: Arama, Tablo Doldurma (Lazy Load), Hesap Makinesi.
    """
    def __init__(self, main_app):
        self.main_app = main_app
        self.currency_manager = main_app.currency_manager
    def _populate_table_in_batches(self, table: QTableWidget, data_list: list, row_filler_func):
        """
        Tabloyu dondurmadan parça parça (Batch) doldurur.
        SQL verileri büyük olsa bile arayüzü kilitlemez.
        """
        table.setRowCount(0)
        total_rows = len(data_list)
        table.setRowCount(total_rows) # Toplam satır sayısını başta ayarla
        if total_rows == 0:
            return
        BATCH_SIZE = 100 # Her döngüde çizilecek satır sayısı (Arttırıldı, modern PC'ler hızlı)
        current_row = 0
        def process_batch():
            nonlocal current_row
            try:
                if table.rowCount() != total_rows:
                    return
            except RuntimeError:
                return
            end_row = min(current_row + BATCH_SIZE, total_rows)
            table.setUpdatesEnabled(False)
            table.blockSignals(True)
            try:
                for i in range(current_row, end_row):
                    row_data = data_list[i]
                    try:
                        row_filler_func(i, row_data)
                    except Exception as e:
                        logger.warning(f"Tablo satır hatası (Index: {i}): {e}")
            finally:
                table.blockSignals(False)
                table.setUpdatesEnabled(True) # Çizimi aç
            current_row = end_row
            if current_row < total_rows:
                QTimer.singleShot(0, process_batch) # 0ms delay = UI loop'una nefes aldır ama bekleme
        process_batch()
    def _perform_search(self):
        self.main_app.search_results_table.setRowCount(0)
        term = self.main_app.search_input.text().lower().strip()
        if len(term) < 2:
            return
        menu_map = [
            ("Genel Durum / Özet", 1, None),
            ("Ürün Alış Ekranı", 2, 0), ("Ürün Satış Ekranı", 2, 1),
            ("Hurda Alış Ekranı", 2, 2), ("Hurda Satış Ekranı", 2, 3),
            ("Ürün Stok Durumu", 3, 0), ("Ürün Tanımlama", 3, 1),
            ("Hurda Stok Durumu", 3, 2), ("Hurda Tanımlama", 3, 3), ("Depo Transferi", 3, 4),
            ("Tamir Takip", 4, None),
            ("Gelir Ekle", 5, 0), ("Gider Ekle", 5, 1), ("Hızlı Cari (Borç Al/Ver)", 5, 2),
            ("Hesaplar Arası Transfer", 5, 3), ("Vadeli İşlemler", 5, 4),
            ("Nakit Kasalar", 6, None), ("Kart Hesapları", 7, None),
            ("Depolar", 8, None),
            ("Müşteri Yönetimi", 9, 0), ("Müşteri Geçmişi / Ekstre", 9, 1),
            ("Cari Hesaplar (Detaylı)", 10, None), ("Döviz İşlemleri", 11, None),
            ("Raporlama", 12, None), ("Ajanda", 13, None),
            ("Hesap Makinesi", 15, None), ("Ayarlar", 16, None)
        ]
        for menu_name, main_idx, sub_idx in menu_map:
            if term in menu_name.lower():
                self._add_search_result("Menü / Ekran", menu_name, "Sayfaya Git", {"type": "navigation", "main_idx": main_idx, "sub_idx": sub_idx})
        for person in self.main_app.person_manager.get_all():
            if term in person.full_name.lower() or term in person.phone_number:
                self._add_search_result(self.main_app.lang["person"], person.full_name, f"Tel: {person.phone_number}", {"type": "person", "id": person.id})
        for product in self.main_app.product_manager.get_all():
            barcode_val = getattr(product, 'barcode', '')
            if term in product.product_name.lower() or (barcode_val and term in barcode_val):
                 self._add_search_result(self.main_app.lang["product"], product.product_name, f"Barkod: {product.barcode}", {"type": "product_def", "id": product.id})
        for scrap in self.main_app.scrap_manager.get_all():
            if term in scrap.product_name.lower():
                self._add_search_result(self.main_app.lang["scrap_definition"], scrap.product_name, f"Ayar: {scrap.karat_purity}", {"type": "scrap_def", "id": scrap.id})
        for ziynet in self.main_app.ziynet_manager.get_all():
            if term in ziynet.product_name.lower():
                self._add_search_result(self.main_app.lang["ziynet_definition"], ziynet.product_name, f"Ayar: {ziynet.karat_purity}", {"type": "ziynet_def", "id": ziynet.id})
        for repair in self.main_app.repair_manager.get_all():
            if term in repair.customer_full_name.lower() or term in repair.product_description.lower():
                self._add_search_result(self.main_app.lang["repair_record"], repair.customer_full_name, f"Ürün: {repair.product_description}", {"type": "repair", "id": repair.id})
        for debt in self.main_app.debt_manager.get_all():
            if term in debt.person_full_name.lower() or term in debt.description.lower():
                self._add_search_result(self.main_app.lang["debt_record"], debt.person_full_name, f"Açıklama: {debt.description}", {"type": "debt", "id": debt.id})
    def _add_search_result(self, result_type, name, details, data):
        row = self.main_app.search_results_table.rowCount()
        self.main_app.search_results_table.insertRow(row)
        self.main_app.search_results_table.setItem(row, 0, QTableWidgetItem(result_type))
        self.main_app.search_results_table.setItem(row, 1, QTableWidgetItem(name))
        self.main_app.search_results_table.setItem(row, 2, QTableWidgetItem(details))
        go_btn = QPushButton(self.main_app._get_icon("SP_ArrowRight"), self.main_app.lang["go_to_record"])
        go_btn.clicked.connect(lambda: self.main_app.navigate_to_item(data))
        self.main_app.search_results_table.setCellWidget(row, 3, go_btn)
    def _on_calc_button_click(self, text):
        current_text = self.main_app.calc_display.text()
        if text == '=':
            try:
                safe_chars = "0123456789.+-*/() "
                expression = "".join([char for char in current_text if char in safe_chars])
                result = str(_safe_calculate_expression(expression))
                self.main_app.calc_display.setText(result)
            except Exception: self.main_app.calc_display.setText("Error")
        elif text == 'C': self.main_app.calc_display.clear()
        elif text == '<-': self.main_app.calc_display.setText(current_text[:-1])
        else: self.main_app.calc_display.setText(current_text + text)
    def convert_currency(self) -> None:
        amount_val = self.main_app.get_float_from_entry(self.main_app.amount_entry, self.main_app.lang["amount"])
        if amount_val is None: return
        from_curr = self.main_app.from_currency_combo.currentText()
        to_curr = self.main_app.to_currency_combo.currentText()
        if not from_curr or not to_curr:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["select_currencies"])
            return
        rate = self.currency_manager.get_exchange_rate(from_curr, to_curr)
        if rate is None:
            self.main_app.result_label.setText(self.main_app.lang["error"])
            QMessageBox.critical(self.main_app, self.main_app.lang["error"], self.main_app.lang["rate_error"])
            return
        converted_amount = amount_val * rate
        self.main_app.result_label.setText(self.currency_manager.format_currency(converted_amount, to_curr))
        self.main_app._show_status_message(f"{format_number(amount_val)} {from_curr} = {format_number(converted_amount)} {to_curr}", 5000)
    def _load_calendar_note(self) -> None:
        if not hasattr(self.main_app, 'calendar'): return
        try:
            selected_date_str = self.main_app.calendar.selectedDate().toString("dd.MM.yyyy")
            note_data = self.main_app.calendar_manager.get_note_data(selected_date_str)
            self.main_app.notes_text.setHtml(note_data.get("html", ""))
            self.main_app.agenda_reminder_checkbox.setChecked(note_data.get("is_reminder", False))
            if hasattr(self.main_app, 'font_combo'):
                self.main_app.font_combo.setCurrentFont(self.main_app.notes_text.currentFont())
            if hasattr(self.main_app, 'font_size_combo'):
                self.main_app.font_size_combo.setCurrentText(str(int(self.main_app.notes_text.fontPointSize())))
            if hasattr(self.main_app, 'bold_btn'):
                self.main_app.bold_btn.setChecked(self.main_app.notes_text.fontWeight() == QFont.Weight.Bold)
        except Exception as e:
            logger.error(f"Ajanda notu yüklenirken hata: {e}")




