import json
import sys
import os
import logging
from pathlib import Path
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Tuple, Any
from collections import defaultdict
from fpdf import FPDF
try:
    import openpyxl
    from openpyxl.styles import Font, Alignment, Border, Side
    from openpyxl.utils import get_column_letter
except ImportError:
    openpyxl = None
from PyQt6.QtWidgets import (
    QMessageBox, QTableWidgetItem, QTableWidget, QHeaderView,
    QFrame, QLabel, QVBoxLayout, QFileDialog
)
from PyQt6.QtGui import QColor, QFont
from PyQt6.QtCore import Qt, QDate
from core.config import BASE_CURRENCY, FONT_PATH, get_app_data_directory, format_number, parse_date_from_display, logger
from core.db import SettingsManager
from modules.cash import CurrencyManager
class ReportPDF(FPDF):
    """Custom FPDF class for generating PDF reports with a header and footer."""
    def __init__(self, title: str, lang: Dict[str, str], *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title_text = title
        self.lang = lang
        self.font_added = False
    
    def add_font_if_needed(self):
        if self.font_added:
            return
        
        # DEĞİŞİKLİK: Karmaşık aramalar yerine config'den gelen yolu kullanıyoruz.
        font_to_use = FONT_PATH # core/config.py'den geliyor (Belgeler/Golder/DejaVuSans.ttf)
        
        if Path(font_to_use).exists():
            try:
                self.add_font('DejaVu', '', font_to_use, uni=True)
                self.add_font('DejaVu', 'B', font_to_use, uni=True)
                self.font_added = True
            except Exception as e:
                logger.error(f"Font yüklenemedi ({font_to_use}): {e}")
        else:
            logger.warning(f"Font bulunamadı: {font_to_use}. Standart font kullanılacak.")

    def header(self):
        """Adds header to each page, including title."""
        self.add_font_if_needed()
        font_family = 'DejaVu' if self.font_added else 'Arial'
        self.set_font(font_family, '', 12)
        self.cell(0, 10, self.title_text, 0, 1, 'C')
        self.ln(10)
    def footer(self):
        """Adds footer with page number to each page."""
        self.set_y(-15)
        self.add_font_if_needed()
        font_family = 'DejaVu' if self.font_added else 'Arial'
        self.set_font(font_family, '', 8)
        self.cell(0, 10, f'{self.lang.get("page", "Page")} {self.page_no()}', 0, 0, 'C')
class ReportGenerator:
    """Base class for report generators."""
    def __init__(self, currency_manager: CurrencyManager, settings_manager: SettingsManager, lang: Dict[str, str]):
        self.currency_manager = currency_manager
        self.settings_manager = settings_manager
        self.lang = lang
        self.display_currency = self.settings_manager.get_setting("display_currency", BASE_CURRENCY)
class PdfReportGenerator(ReportGenerator):
    """Generates PDF reports."""
    def generate_report(self, report_type: str, headers: List[str], data_rows: List[List[str]],
                        summary_data: Dict[str, str], filename: str) -> Optional[str]:
        try:
            pdf = ReportPDF(title=report_type, lang=self.lang)
            pdf.add_font_if_needed()
            if not pdf.font_added:
                pdf.set_font("Arial", size=8)
            pdf.set_auto_page_break(auto=True, margin=15)
            pdf.add_page()
            font_family = 'DejaVu' if pdf.font_added else 'Arial'
            pdf.set_font(font_family, size=8)
            if not data_rows:
                pdf.cell(0, 10, self.lang["pdf_no_data"], 0, 1, 'C')
            else:
                col_widths = self._calculate_column_widths(headers, data_rows, pdf.w - 20, pdf)
                pdf.set_font(font_family, style='B')
                for i, header in enumerate(headers):
                    pdf.cell(col_widths[i], 8, header, 1, 0, 'C')
                pdf.ln()
                pdf.set_font(font_family, style='')
                for row in data_rows:
                    self._draw_table_row(pdf, row, col_widths)
            pdf.ln(10)
            if summary_data:
                pdf.set_font(font_family, style='B', size=10)
                for key, val in summary_data.items():
                    pdf.cell(0, 6, f"{key}: {val}", 0, 1, 'L')
            pdf.output(filename)
            QMessageBox.information(None, self.lang["success"], self.lang["pdf_created"].format(filename=Path(filename).name))
            return filename
        except Exception as e:
            logger.critical(f"Error generating PDF report '{filename}': {e}", exc_info=True)
            QMessageBox.critical(None, self.lang["error"], f"PDF oluşturulurken hata oluştu:\n{e}\n\nLütfen açık olan PDF dosyalarını kapatıp tekrar deneyin.")
            return None
    def _draw_table_row(self, pdf: FPDF, row: List[Any], col_widths: List[float], line_height: float = 6) -> None:
        row_height = line_height
        cell_texts: List[str] = []

        for i, cell_data in enumerate(row):
            if i >= len(col_widths):
                break
            cell_text = str(cell_data)
            cell_texts.append(cell_text)
            wrapped_lines = pdf.multi_cell(col_widths[i], line_height, cell_text, border=0, align='L', split_only=True)
            row_height = max(row_height, len(wrapped_lines) * line_height)

        if pdf.get_y() + row_height > pdf.page_break_trigger:
            pdf.add_page()

        start_x = pdf.get_x()
        start_y = pdf.get_y()
        current_x = start_x

        # Manually position cells so table rendering works in both pyfpdf and fpdf2.
        for i, cell_text in enumerate(cell_texts):
            col_width = col_widths[i]
            pdf.rect(current_x, start_y, col_width, row_height)
            pdf.set_xy(current_x, start_y)
            pdf.multi_cell(col_width, line_height, cell_text, border=0, align='L')
            current_x += col_width
            pdf.set_xy(current_x, start_y)

        pdf.set_xy(start_x, start_y + row_height)

    def _calculate_column_widths(self, headers: List[str], data_rows: List[List[str]], total_width: float, pdf: FPDF) -> List[float]:
        num_cols = len(headers)
        if num_cols == 0: return []
        max_widths = [pdf.get_string_width(h) + 6 for h in headers]
        for row in data_rows:
            for i, cell in enumerate(row):
                if i < len(max_widths):
                    max_widths[i] = max(max_widths[i], pdf.get_string_width(str(cell)) + 6)
        current_total_width = sum(max_widths)
        if current_total_width > total_width and current_total_width > 0:
            factor = total_width / current_total_width
            widths = [w * factor for w in max_widths]
        else:
             widths = max_widths
        return widths
class ExcelReportGenerator(ReportGenerator):
    """Generates Excel reports."""
    def generate_report(self, report_type: str, headers: List[str], data_rows: List[List[str]],
                        summary_data: Dict[str, str], filename: str) -> Optional[str]:
        if openpyxl is None:
            QMessageBox.critical(None, self.lang["error"], self.lang["excel_library_missing"])
            return None
        try:
            wb = openpyxl.Workbook()
            ws = wb.active
            ws.title = report_type[:30]
            header_font = Font(bold=True)
            header_alignment = Alignment(horizontal='center', vertical='center')
            header_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
            ws.append(headers)
            for col_idx, cell in enumerate(ws[1], 1):
                cell.font = header_font
                cell.alignment = header_alignment
                cell.border = header_border
            for row_data in data_rows:
                ws.append(row_data)
            for col_idx, column_cells in enumerate(ws.columns, 1):
                max_length = 0
                column = get_column_letter(col_idx)
                for cell in column_cells:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except: pass
                adjusted_width = (max_length + 2)
                ws.column_dimensions[column].width = adjusted_width
            if summary_data:
                ws.append([])
                for label, value in summary_data.items():
                    ws.append([label, value])
                    ws.cell(row=ws.max_row, column=1).font = Font(bold=True)
            wb.save(filename)
            QMessageBox.information(None, self.lang["success"], self.lang["pdf_created"].format(filename=Path(filename).name))
            return filename
        except Exception as e:
            logger.critical(f"Error generating Excel report '{filename}': {e}", exc_info=True)
            QMessageBox.critical(None, self.lang["error"], f"Excel could not be created: {e}")
            return None
class ReportManager:
    """
    Raporlama, filtreleme, özet oluşturma ve dışa aktarma (PDF/Excel) işlemlerini yönetir.
    JewelryStoreApp sınıfından ayrılarak kodun modüler olmasını sağlar.
    """
    def __init__(self, main_app):
        self.main_app = main_app
        self.currency_manager = main_app.currency_manager
    def _report_today(self):
        self._set_report_date_range(0, 0)
    def _report_yesterday(self):
        self._set_report_date_range(1, 1)
    def _report_this_week(self):
        self._set_report_date_range(datetime.now().weekday(), 0)
    def _report_this_month(self):
        self._set_report_date_range(datetime.now().day - 1, 0)
    def _report_this_year(self):
        self.main_app.report_start_date_entry.setDate(QDate(datetime.now().year, 1, 1))
        self.main_app.report_end_date_entry.setDate(datetime.now().date())
        self._filter_report()
    def _set_report_date_range(self, start_delta_days, end_delta_days):
        """Rapor tarih filtrelerini ayarlar (QDateEdit uyumlu)."""
        today = datetime.now()
        start_dt = today - timedelta(days=start_delta_days)
        end_dt = today - timedelta(days=end_delta_days)
        self.main_app.report_start_date_entry.setDate(QDate(start_dt.year, start_dt.month, start_dt.day))
        self.main_app.report_end_date_entry.setDate(QDate(end_dt.year, end_dt.month, end_dt.day))
        self._filter_report()  # <--- BURASI DÜZELTİLDİ
    def _report_type_changed(self) -> None:
        report_type = self.main_app.report_type_combo.currentText()
        is_customer_report = report_type == self.main_app.lang["customer_statement_report"]
        is_stock_report = report_type in [self.main_app.lang["product_stock_report"], self.main_app.lang["scrap_stock_report"], self.main_app.lang["ziynet_stock_report"]]
        self.main_app.report_customer_combo.setVisible(is_customer_report)
        self.main_app.report_date_filter_widget.setVisible(not is_stock_report)
        self._filter_report()
    def _filter_report(self) -> None:
        if not hasattr(self.main_app, 'report_depot_combo') or not hasattr(self.main_app, 'report_type_combo'):
            return
        try:
            report_type = self.main_app.report_type_combo.currentText()
        except RuntimeError:
            return
        self._hide_cards()
        show_date = True
        show_customer = False
        show_depot = False
        if report_type == self.main_app.lang["profit_loss_report"]:
            self._generate_profit_loss_report()
        elif report_type == self.main_app.lang["cash_flow_report"]:
            self._generate_cash_flow_report()
        elif report_type in [self.main_app.lang["product_stock_report"], self.main_app.lang["scrap_stock_report"], self.main_app.lang["ziynet_stock_report"]]:
            show_date = False
            show_depot = True
            try:
                curr_text = self.main_app.report_depot_combo.currentText()
                self.main_app.report_depot_combo.blockSignals(True)
                self.main_app.report_depot_combo.clear()
                self.main_app.report_depot_combo.addItem(self.main_app.lang.get("show_all", "Tümünü Göster"))
                self.main_app.report_depot_combo.addItems(self.main_app.depot_manager.get_depot_names())
                if curr_text:
                    self.main_app.report_depot_combo.setCurrentText(curr_text)
                else:
                    self.main_app.report_depot_combo.setCurrentIndex(0)
                self.main_app.report_depot_combo.blockSignals(False)
            except RuntimeError:
                pass
            if report_type == self.main_app.lang["product_stock_report"]:
                stock_type = "Product"
            elif report_type == self.main_app.lang["ziynet_stock_report"]:
                stock_type = "Ziynet"
            else:
                stock_type = "Scrap"
            self._generate_stock_report(stock_type)
        elif report_type == self.main_app.lang["customer_statement_report"]:
            show_customer = True
            self._generate_customer_statement_report()
        elif report_type == "Genel Hizmet Raporu":
            self._generate_service_report()
        try:
            if hasattr(self.main_app, 'report_date_filter_widget'): self.main_app.report_date_filter_widget.setVisible(show_date)
            if hasattr(self.main_app, 'report_customer_combo'): self.main_app.report_customer_combo.setVisible(show_customer)
            if hasattr(self.main_app, 'report_depot_combo'): self.main_app.report_depot_combo.setVisible(show_depot)
        except RuntimeError:
            pass
        self._hide_cards()
        show_date = True
        show_customer = False
        show_depot = False
        if report_type == self.main_app.lang["profit_loss_report"]:
            self._generate_profit_loss_report()
        elif report_type == self.main_app.lang["cash_flow_report"]:
            self._generate_cash_flow_report()
        elif report_type == self.main_app.lang.get("rep_tax_report", "Tax Report"):
            self._generate_tax_report()
        elif report_type in [self.main_app.lang["product_stock_report"], self.main_app.lang["scrap_stock_report"], self.main_app.lang["ziynet_stock_report"]]:
            show_date = False
            show_depot = True
            try:
                curr_text = self.main_app.report_depot_combo.currentText()
                self.main_app.report_depot_combo.blockSignals(True)
                self.main_app.report_depot_combo.clear()
                self.main_app.report_depot_combo.addItem(self.main_app.lang.get("show_all", "Tümünü Göster"))
                self.main_app.report_depot_combo.addItems(self.main_app.depot_manager.get_depot_names())
                if curr_text:
                    self.main_app.report_depot_combo.setCurrentText(curr_text)
                else:
                    self.main_app.report_depot_combo.setCurrentIndex(0)
                self.main_app.report_depot_combo.blockSignals(False)
            except RuntimeError:
                pass
            if report_type == self.main_app.lang["product_stock_report"]:
                stock_type = "Product"
            elif report_type == self.main_app.lang["ziynet_stock_report"]:
                stock_type = "Ziynet"
            else:
                stock_type = "Scrap"
            self._generate_stock_report(stock_type)
        elif report_type == self.main_app.lang["customer_statement_report"]:
            show_customer = True
            self._generate_customer_statement_report()
        elif report_type == "Genel Hizmet Raporu":
            self._generate_service_report()
        try:
            if hasattr(self.main_app, 'report_date_filter_widget'): self.main_app.report_date_filter_widget.setVisible(show_date)
            if hasattr(self.main_app, 'report_customer_combo'): self.main_app.report_customer_combo.setVisible(show_customer)
            if hasattr(self.main_app, 'report_depot_combo'): self.main_app.report_depot_combo.setVisible(show_depot)
        except RuntimeError:
            pass

            
    def _get_report_date_range(self) -> Tuple[Optional[datetime], Optional[datetime]]:
        """Rapor için seçili tarih aralığını datetime nesnesi olarak döndürür."""
        try:
            q_start = self.main_app.report_start_date_entry.date()
            q_end = self.main_app.report_end_date_entry.date()
            start_date = datetime(q_start.year(), q_start.month(), q_start.day())
            end_date = datetime(q_end.year(), q_end.month(), q_end.day())
            return start_date, end_date
        except Exception as e:
            logger.error(f"Tarih alma hatası: {e}")
            return None, None
           
    def _generate_tax_report(self):
        start_date, end_date = self._get_report_date_range()
        if not start_date: return
        
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        table.setColumnCount(5)
        table.setHorizontalHeaderLabels(["Tarih", "İşlem", "Matrah", "Vergi", "Genel Toplam"])
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        
        # Verileri çek
        all_recs = self.main_app.tax_application_manager.get_all()
        
        total_tax = 0.0
        row = 0
        for rec in all_recs:
            # Tarih filtresi (String parse ederek)
            try:
                rec_date = datetime.fromisoformat(rec.created_at)
                if not (start_date <= rec_date <= end_date.replace(hour=23, minute=59)):
                    continue
            except: continue
            
            table.insertRow(row)
            table.setItem(row, 0, QTableWidgetItem(rec.created_at[:16].replace('T', ' ')))
            table.setItem(row, 1, QTableWidgetItem(rec.source_table)) # sales vs.
            table.setItem(row, 2, QTableWidgetItem(format_number(rec.subtotal)))
            table.setItem(row, 3, QTableWidgetItem(format_number(rec.tax_total)))
            table.setItem(row, 4, QTableWidgetItem(format_number(rec.grand_total)))
            
            total_tax += float(rec.tax_total)
            row += 1
            
        self._update_card(self.main_app.card1, "Raporlanan Vergi", format_number(total_tax), "#D32F2F")
        self._update_card(self.main_app.card2, "İşlem Sayısı", str(row))
        self._update_card(self.main_app.card3, "-", "-")
        
        # (Burada data_manager üzerinden verileri çekip tabloyu dolduran kod olacak)
        # Özet kartlarını güncelle: Toplam Matrah, Toplam Vergi, Toplam İşlem
    def _setup_report_summary_labels(self, label1, label2, label3):
        self.main_app.report_summary_label1.setText(f"{label1}:")
        self.main_app.report_summary_label2.setText(f"{label2}:")
        self.main_app.report_summary_label3.setText(f"{label3}:")
    def _update_report_summary_values(self, value1, value2, value3):
        self.main_app.report_summary_value1.setText(value1)
        self.main_app.report_summary_value2.setText(value2)
        self.main_app.report_summary_value3.setText(value3)
    def _update_card(self, card, title, value, color=None):
        card.findChild(QLabel, "cardTitle").setText(title)
        val_lbl = card.findChild(QLabel, "cardValue")
        val_lbl.setText(value)
        if color: val_lbl.setStyleSheet(f"font-size: 18px; color: {color}; font-weight: bold;")
        card.setVisible(True)
    def _hide_cards(self):
        self.main_app.card1.setVisible(False)
        self.main_app.card2.setVisible(False)
        self.main_app.card3.setVisible(False)
    def create_summary_card(self):
        frame = QFrame()
        frame.setStyleSheet("background-color: rgba(0,0,0,0.05); border-radius: 8px; border: 1px solid #ccc;")
        frame.setMinimumWidth(200)
        l = QVBoxLayout(frame)
        title = QLabel("-")
        title.setObjectName("cardTitle")
        title.setStyleSheet("font-size: 12px; color: #666; font-weight: bold;")
        val = QLabel("0.00")
        val.setObjectName("cardValue")
        theme_name = self.main_app.settings_manager.get_setting('theme_name')
        primary_color = self.main_app.all_themes.get(theme_name, {}).get('primary', '#000000')
        val.setStyleSheet(f"font-size: 18px; color: {primary_color}; font-weight: bold;")
        l.addWidget(title, alignment=Qt.AlignmentFlag.AlignCenter)
        l.addWidget(val, alignment=Qt.AlignmentFlag.AlignCenter)
        return frame
    def _generate_cash_flow_report(self) -> None:
        start_date, end_date = self._get_report_date_range()
        if not start_date or not end_date: return
        all_transactions = []
        for manager in list(self.main_app.cash_managers.values()) + list(self.main_app.card_managers.values()):
            all_transactions.extend(manager.get_filtered_transactions(start_date, end_date))
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        table.setColumnCount(4)
        table.setHorizontalHeaderLabels([self.main_app.lang["date"], self.main_app.lang["transaction_type"], self.main_app.lang["amount"], self.main_app.lang["description"]])
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        total_income_raw = 0.0
        total_expense_raw = 0.0
        sorted_trans = sorted(all_transactions, key=lambda x: (parse_date_from_display(x.transaction_date) or datetime.min, x.created_at))
        table.setRowCount(len(sorted_trans))
        for row, transaction in enumerate(sorted_trans):
            table.setItem(row, 0, QTableWidgetItem(transaction.transaction_date))
            table.setItem(row, 1, QTableWidgetItem(self.main_app.lang.get(transaction.transaction_type.lower(), transaction.transaction_type)))
            amt_str = f"{format_number(transaction.original_amount)} {transaction.currency}"
            amt_item = QTableWidgetItem(amt_str)
            if transaction.transaction_type == "Income":
                amt_item.setForeground(QColor("green"))
                total_income_raw += transaction.original_amount
            else:
                amt_item.setForeground(QColor("red"))
                total_expense_raw += transaction.original_amount
            table.setItem(row, 2, amt_item)
            table.setItem(row, 3, QTableWidgetItem(transaction.description))
        disp_income = total_income_raw
        disp_expense = total_expense_raw
        disp_net = disp_income - disp_expense
        self._update_card(
            self.main_app.card1,
            self.main_app.lang['filtered_income_sales'],
            format_number(disp_income),
            "#388E3C"
        )
        self._update_card(
            self.main_app.card2,
            self.main_app.lang['filtered_expense_cost'],
            format_number(disp_expense),
            "#D32F2F"
        )
        net_color = "#388E3C" if disp_net >= 0 else "#D32F2F"
        self._update_card(
            self.main_app.card3,
            self.main_app.lang['filtered_net'],
            format_number(disp_net),
            net_color
        )
    def _generate_profit_loss_report(self) -> None:
        start_date = self.main_app.report_start_date_entry.date().toPyDate()
        end_date = self.main_app.report_end_date_entry.date().toPyDate()
        filtered_sales = [
            s for s in self.main_app.sale_manager.get_all()
            if start_date <= datetime.strptime(s.sale_date, "%d.%m.%Y").date() <= end_date
        ]
        total_revenue_raw = 0.0
        total_cogs_raw = 0.0
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        table.setColumnCount(6)
        table.setHorizontalHeaderLabels(["Tarih", "Müşteri", "Satış Tutarı", "Maliyet", "Brüt Kâr", "Marj %"])
        row = 0
        for sale in filtered_sales:
            sale_base = self.currency_manager._convert_to_base_currency(sale.total_amount, sale.currency)
            cost_base = 0.0
            for prod in sale.products_sold:
                qty = prod.get('quantity', 0)
                unit_cost = prod.get('cost_price_base', 0)
                cost_base += (qty * unit_cost)
            gross_profit = sale_base - cost_base
            margin = (gross_profit / sale_base * 100) if sale_base > 0 else 0
            total_revenue_raw += sale_base
            total_cogs_raw += cost_base
            table.insertRow(row)
            table.setItem(row, 0, QTableWidgetItem(sale.sale_date))
            table.setItem(row, 1, QTableWidgetItem(sale.customer_full_name))
            table.setItem(row, 2, QTableWidgetItem(self.currency_manager.format_currency(
                sale_base, self.main_app.display_currency)))
            table.setItem(row, 3, QTableWidgetItem(format_number(cost_base)))
            table.setItem(row, 4, QTableWidgetItem(format_number(gross_profit)))
            table.setItem(row, 5, QTableWidgetItem(f"%{margin:.1f}"))
            row += 1
        total_gross_profit = total_revenue_raw - total_cogs_raw
        total_expenses_raw = 0.0
        for mgr in list(self.main_app.cash_managers.values()) + list(self.main_app.card_managers.values()):
            for trans in mgr.get_filtered_transactions(datetime.combine(start_date, datetime.min.time()), datetime.combine(end_date, datetime.max.time())):
                if trans.transaction_type == "Expense":
                    total_expenses_raw += trans.original_amount
        net_profit_raw = total_gross_profit - total_expenses_raw
        self._update_card(
            self.main_app.card1,
            self.main_app.lang.get("total_revenue_sales", "Total Revenue (Sales)"),
            self.currency_manager.format_currency(total_revenue_raw, self.main_app.display_currency),
            "#1976D2",
        )
        self._update_card(
            self.main_app.card2,
            self.main_app.lang.get("gross_profit_sales", "Gross Profit (Sales)"),
            self.currency_manager.format_currency(total_gross_profit, self.main_app.display_currency),
            "#388E3C",
        )
        net_color = "#388E3C" if net_profit_raw >= 0 else "#D32F2F"
        self._update_card(
            self.main_app.card3,
            self.main_app.lang.get("net_profit_expenses", "NET PROFIT (Expenses: {expenses})").format(
                expenses=format_number(total_expenses_raw)
            ),
            self.currency_manager.format_currency(net_profit_raw, self.main_app.display_currency),
            net_color,
        )
    def _generate_stock_report(self, stock_type: str):
        """
        Stok Raporu Oluşturur.
        FAZ 7: UI Donmalarını önlemek için processEvents eklendi.
        """
        from PyQt6.QtWidgets import QApplication
        manager = self.main_app.stock_manager if stock_type == "Product" else self.main_app.scrap_stock_manager
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        headers = [
            self.main_app.lang["product_type"],
            self.main_app.lang["depot"],
            self.main_app.lang["quantity"],
            f"{self.main_app.lang['average_purchase']} (Giriş)",
            f"{self.main_app.lang['total_value']} (Giriş)"
        ]
        table.setColumnCount(len(headers))
        table.setHorizontalHeaderLabels(headers)
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        all_stock = manager.get_all()
        selected_depot = self.main_app.report_depot_combo.currentText()
        show_all_text = self.main_app.lang.get("show_all", "Tümünü Göster")
        accessible_depots = self.main_app._get_accessible_depots()
        filtered_stock = []
        for s in all_stock:
            if s.depot_name not in accessible_depots:
                continue
            if self.main_app.report_depot_combo.isVisible() and selected_depot and selected_depot != show_all_text:
                if s.depot_name != selected_depot:
                    continue
            filtered_stock.append(s)
        sorted_stock = sorted(filtered_stock, key=lambda s: (s.item_type.lower(), s.depot_name.lower()))
        table.setRowCount(len(sorted_stock))
        total_stock_value_raw = 0.0
        total_quantity = 0.0
        for row, item in enumerate(sorted_stock):
            if row % 20 == 0: # Her 20 satırda bir UI güncelle
                QApplication.processEvents()
            item_value_raw = item.quantity * item.purchase_price_base
            total_stock_value_raw += item_value_raw
            total_quantity += item.quantity
            table.setItem(row, 0, QTableWidgetItem(item.item_type))
            table.setItem(row, 1, QTableWidgetItem(item.depot_name))
            table.setItem(row, 2, QTableWidgetItem(format_number(item.quantity, 3)))
            table.setItem(row, 3, QTableWidgetItem(format_number(item.purchase_price_base)))
            table.setItem(row, 4, QTableWidgetItem(format_number(item_value_raw)))
        depot_title = (
            selected_depot
            if (self.main_app.report_depot_combo.isVisible() and selected_depot != show_all_text)
            else self.main_app.lang.get("all_authorized_depots", "All Authorized Depots")
        )
        self._update_card(
            self.main_app.card1,
            f"{depot_title} - {self.main_app.lang.get('variety_count', 'Variety')}",
            str(len(sorted_stock)),
        )
        self._update_card(
            self.main_app.card2,
            self.main_app.lang.get("total_quantity", "Total Quantity"),
            format_number(total_quantity, 3),
        )
        self._update_card(
            self.main_app.card3,
            self.main_app.lang.get("total_raw_value", "Total Value (Raw)"),
            self.currency_manager.format_currency(total_stock_value_raw, self.main_app.display_currency),
            "#D4AF37",
        )
    def _generate_customer_statement_report(self) -> None:
        """
        Müşteri ekstresi raporunu oluşturur.
        GÜNCELLEME: Cari Hesaplar sekmesiyle aynı bakiye hesaplama mantığını kullanır.
        Bedelli işlemleri doğru yorumlar ve ürünleri değil, bedelleri borç/alacak olarak işler.
        """
        customer_name = self.main_app.report_customer_combo.currentText()
        if not customer_name:
            self.main_app.report_transactions_table.setRowCount(0)
            self._hide_cards()
            return
        start_date, end_date = self._get_report_date_range()
        if not start_date or not end_date: return
        start_dt = start_date
        end_dt = end_date.replace(hour=23, minute=59, second=59)
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        table.setColumnCount(6)
        table.setHorizontalHeaderLabels([self.main_app.lang["date"], self.main_app.lang["transaction_type"], self.main_app.lang["description"], self.main_app.lang["debt_col"], self.main_app.lang["credit_col"], self.main_app.lang["balance_col"]])
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        all_events = self.main_app.customer_history_manager.get_all()
        customer_events_all_time = sorted(
            [e for e in all_events if e.person_full_name == customer_name],
            key=lambda x: (parse_date_from_display(x.event_date) or datetime.min)
        )
        customer_events_in_range = [
            e for e in customer_events_all_time
            if start_dt <= (parse_date_from_display(e.event_date) or datetime.min) <= end_dt
        ]
        running_balances = defaultdict(float)
        event_balances = {} # Her olaydan SONRAKİ bakiyeyi saklamak için
        for event in customer_events_all_time:
            if event.description and event.description.strip().startswith('{'):
                try:
                    data = json.loads(event.description)
                    items = data.get('exchange_items', [])
                    mode = data.get('mode', '')
                    direction = -1 if ('Out' in mode or 'Sale' in mode) else (1 if ('In' in mode or 'Purchase' in mode) else 0)
                    for item in items:
                        try:
                            amt = float(item.get('amount', 0))
                            typ = item.get('type')
                            if typ and amt > 0:
                                running_balances[typ] += (amt * direction)
                        except: pass
                except:
                    pass # Hatalı JSON ise atla
            else:
                if event.currency and event.currency != "Karma":
                    running_balances[event.currency] += (event.debt_original - event.credit_original)
            event_balances[event.id] = running_balances.copy()
        table.setRowCount(len(customer_events_in_range))
        for row, event in enumerate(reversed(customer_events_in_range)):
            debt_text = ""
            credit_text = ""
            if event.description and event.description.strip().startswith('{'):
                try:
                    data = json.loads(event.description)
                    items = data.get('exchange_items', [])
                    mode = data.get('mode', '')
                    is_out = 'Out' in mode or 'Sale' in mode
                    if items:
                        first_item = items[0]
                        amount = first_item['amount']
                        currency = first_item['type']
                        if is_out:
                            credit_text = f"{format_number(amount)} {currency}"
                        else:
                            debt_text = f"{format_number(amount)} {currency}"
                        if len(items) > 1:
                            debt_text += " (+...)"
                            credit_text += " (+...)"
                except: pass # Hata varsa boş kalsın
            else:
                if event.debt_original > 0:
                    debt_text = f"{format_number(event.debt_original)} {event.currency}"
                if event.credit_original > 0:
                    credit_text = f"{format_number(event.credit_original)} {event.currency}"
            balance_at_event = event_balances.get(event.id, {})
            balance_texts = [f"{format_number(bal)} {curr}" for curr, bal in sorted(balance_at_event.items()) if abs(bal) > 0.001]
            balance_str = " | ".join(balance_texts) if balance_texts else "0"
            event_date_obj = parse_date_from_display(event.event_date)
            date_text = event_date_obj.strftime("%d.%m.%Y %H:%M") if event_date_obj else str(event.event_date or "")
            table.setItem(row, 0, QTableWidgetItem(date_text))
            display_event_type = event.event_type
            crm_manager = getattr(self.main_app, "crm_manager", None)
            if crm_manager and hasattr(crm_manager, "_get_display_event_type"):
                try:
                    display_event_type = crm_manager._get_display_event_type(event)
                except Exception:
                    display_event_type = event.event_type
            table.setItem(row, 1, QTableWidgetItem(display_event_type))
            table.setItem(row, 2, QTableWidgetItem(event.description))
            d_item = QTableWidgetItem(debt_text); d_item.setForeground(QColor("#D32F2F"))
            table.setItem(row, 3, d_item)
            c_item = QTableWidgetItem(credit_text); c_item.setForeground(QColor("#388E3C"))
            table.setItem(row, 4, c_item)
            b_item = QTableWidgetItem(balance_str); b_item.setFont(QFont("Segoe UI", 9, QFont.Weight.Bold))
            table.setItem(row, 5, b_item)
        net_balance_str = " | ".join([f"{format_number(v)} {k}" for k, v in sorted(running_balances.items()) if abs(v) > 0.001]) or "0"
        total_debt = defaultdict(float)
        total_credit = defaultdict(float)
        for event in customer_events_in_range:
            if event.description and event.description.strip().startswith('{'):
                try:
                    data = json.loads(event.description)
                    items = data.get('exchange_items', [])
                    mode = data.get('mode', '')
                    is_out = 'Out' in mode or 'Sale' in mode
                    is_in = 'In' in mode or 'Purchase' in mode
                    for item in items:
                        if is_in: total_debt[item['type']] += item['amount']
                        if is_out: total_credit[item['type']] += item['amount']
                except: pass
            else: # Standart işlemler
                if event.currency:
                    total_debt[event.currency] += event.debt_original
                    total_credit[event.currency] += event.credit_original
        total_credit_str = " | ".join([f"{format_number(v)} {k}" for k, v in sorted(total_credit.items()) if v > 0]) or "0"
        total_debt_str = " | ".join([f"{format_number(v)} {k}" for k, v in sorted(total_debt.items()) if v > 0]) or "0"
        self._update_card(
            self.main_app.card1,
            self.main_app.lang.get("filtered_total_receivable_in", "Filtered Total Receivable (In)"),
            total_credit_str,
            "#388E3C",
        )
        self._update_card(
            self.main_app.card2,
            self.main_app.lang.get("filtered_total_payable_out", "Filtered Total Payable (Out)"),
            total_debt_str,
            "#D32F2F",
        )
        self._update_card(
            self.main_app.card3,
            self.main_app.lang.get("general_net_balance", "General Net Balance"),
            net_balance_str,
            "#1976D2",
        )
    def _generate_depot_summary_report(self):
        all_stock = self.main_app.stock_manager.get_all() + self.main_app.scrap_stock_manager.get_all() + self.main_app.ziynet_stock_manager.get_all()
        depot_stats = defaultdict(lambda: {'items_count': 0, 'total_qty': 0.0, 'total_value_base': 0.0})
        for item in all_stock:
            d_name = item.depot_name
            depot_stats[d_name]['items_count'] += 1
            depot_stats[d_name]['total_qty'] += item.quantity
            depot_stats[d_name]['total_value_base'] += (item.quantity * item.purchase_price_base)
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        table.setColumnCount(4)
        table.setHorizontalHeaderLabels(["Depo Adı", "Kalem Sayısı", "Toplam Miktar", f"Toplam Stok Değeri ({self.main_app.display_currency})"])
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        grand_total_value_base = 0.0
        row = 0
        for depot_name, stats in sorted(depot_stats.items()):
            table.insertRow(row)
            val_disp = self.currency_manager._convert_to_display_currency(stats['total_value_base'], self.main_app.display_currency)
            grand_total_value_base += stats['total_value_base']
            table.setItem(row, 0, QTableWidgetItem(depot_name))
            table.setItem(row, 1, QTableWidgetItem(str(stats['items_count'])))
            table.setItem(row, 2, QTableWidgetItem(format_number(stats['total_qty'], 3)))
            table.setItem(row, 3, QTableWidgetItem(format_number(val_disp)))
            row += 1
        grand_total_disp = self.currency_manager._convert_to_display_currency(grand_total_value_base, self.main_app.display_currency)
        self._update_card(self.main_app.card1, "Toplam Depo Sayısı", str(len(depot_stats)))
        self._update_card(self.main_app.card2, "Toplam Kalem", str(len(all_stock)))
        self._update_card(self.main_app.card3, "Genel Stok Değeri", self.currency_manager.format_currency(grand_total_disp, self.main_app.display_currency), "#D4AF37")
    def _export_report(self, export_type: str) -> None:
        """
        Raporu dışa aktarır.
        FAZ 8: İşlem süresince 'Bekleyiniz' imleci gösterilir.
        """
        from PyQt6.QtWidgets import QApplication, QFileDialog, QLabel
        from PyQt6.QtCore import Qt
        QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
        try:
            report_type_text = self.main_app.report_type_combo.currentText()
            start_date_str = self.main_app.report_start_date_entry.text()
            end_date_str = self.main_app.report_end_date_entry.text()
            default_filename_base = f"{report_type_text.replace(' ', '_').replace('/', '-')}_{start_date_str.replace('.', '-')}_{end_date_str.replace('.', '-')}"
            table = self.main_app.report_transactions_table
            headers = [table.horizontalHeaderItem(i).text() for i in range(table.columnCount())]
            data_rows = [[table.item(row, col).text() if table.item(row, col) else "" for col in range(table.columnCount())] for row in range(table.rowCount())]
            summary_data = {}
            try:
                if self.main_app.card1.isVisible():
                    summary_data["ÖZET 1"] = f"{self.main_app.card1.findChild(QLabel, 'cardTitle').text()}: {self.main_app.card1.findChild(QLabel, 'cardValue').text()}"
                if self.main_app.card2.isVisible():
                    summary_data["ÖZET 2"] = f"{self.main_app.card2.findChild(QLabel, 'cardTitle').text()}: {self.main_app.card2.findChild(QLabel, 'cardValue').text()}"
                if self.main_app.card3.isVisible():
                    summary_data["ÖZET 3"] = f"{self.main_app.card3.findChild(QLabel, 'cardTitle').text()}: {self.main_app.card3.findChild(QLabel, 'cardValue').text()}"
            except:
                pass
            QApplication.restoreOverrideCursor()
            if export_type == "pdf":
                filename, _ = QFileDialog.getSaveFileName(self.main_app, self.main_app.lang["export_pdf"], f"{default_filename_base}.pdf", "PDF Files (*.pdf)")
                if filename:
                    QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor) # Tekrar bekleme
                    self.main_app.pdf_report_generator.generate_report(report_type_text, headers, data_rows, summary_data, filename)
            elif export_type == "excel":
                filename, _ = QFileDialog.getSaveFileName(self.main_app, self.main_app.lang["export_excel"], f"{default_filename_base}.xlsx", "Excel Files (*.xlsx)")
                if filename:
                    QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor) # Tekrar bekleme
                    self.main_app.excel_report_generator.generate_report(report_type_text, headers, data_rows, summary_data, filename)
        except Exception as e:
            logger.error(f"Rapor export hatası: {e}")
            QMessageBox.critical(self.main_app, "Hata", f"Rapor oluşturulamadı: {e}")
        finally:
            QApplication.restoreOverrideCursor()
    def _generate_service_report(self) -> None:
        """Hizmetler raporunu oluşturur."""
        start_date, end_date = self._get_report_date_range()
        if not start_date or not end_date: return
        from modules.services import ServiceManager
        temp_manager = ServiceManager(self.main_app.data_manager)
        all_jobs = temp_manager.jobs
        all_defs = {d.id: d.name for d in temp_manager.definitions}
        table = self.main_app.report_transactions_table
        table.setRowCount(0)
        table.setColumnCount(6)
        table.setHorizontalHeaderLabels(["Tarih", "Hizmet Türü", "Müşteri", "Açıklama", "Tutar", "Durum"])
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        filtered_jobs = []
        total_fee_base = 0.0
        for job in all_jobs:
            try:
                job_date = datetime.strptime(job.start_date, "%d.%m.%Y")
                if start_date <= job_date <= end_date:
                    filtered_jobs.append(job)
            except ValueError:
                continue
        sorted_jobs = sorted(filtered_jobs, key=lambda x: datetime.strptime(x.start_date, "%d.%m.%Y"), reverse=True)
        table.setRowCount(len(sorted_jobs))
        status_counts = defaultdict(int)
        for row, job in enumerate(sorted_jobs):
            service_name = all_defs.get(job.service_def_id, "Bilinmeyen")
            table.setItem(row, 0, QTableWidgetItem(job.start_date))
            table.setItem(row, 1, QTableWidgetItem(service_name))
            table.setItem(row, 2, QTableWidgetItem(job.customer_name))
            table.setItem(row, 3, QTableWidgetItem(job.description))
            fee_str = self.currency_manager.format_currency(job.fee, job.currency)
            table.setItem(row, 4, QTableWidgetItem(fee_str))
            status_item = QTableWidgetItem(job.status)
            if job.status == "Teslim Edildi": status_item.setForeground(QColor("gray"))
            elif job.status == "Hazır": status_item.setForeground(QColor("green"))
            table.setItem(row, 5, status_item)
            fee_base = self.currency_manager._convert_to_base_currency(job.fee, job.currency)
            total_fee_base += fee_base
            status_counts[job.status] += 1
        total_fee_disp = self.currency_manager._convert_to_display_currency(total_fee_base, self.main_app.display_currency)
        self._update_card(self.main_app.card1, "Toplam Hizmet", str(len(sorted_jobs)))
        self._update_card(self.main_app.card2, "Teslim Edilen", str(status_counts.get("Teslim Edildi", 0)))
        self._update_card(self.main_app.card3, "Toplam Tahmini Tutar", self.currency_manager.format_currency(total_fee_disp, self.main_app.display_currency), "#388E3C")




