Create a GUI fopr making invoices that look really good making both the invoices and the GUI look as polished as possible and have the invoices customizable also adding a financial statement overview tab and have both statements and invoices exportable as pdf and excel files. Add a tab where the user can input his company details (this must be remembered and also a tab for adding clients with their details (this should also be remembered) The line items that get added should also be remembered for future invoices. Please iterate improving and upgrading this program a few times and then provide the full snip of code aiming for a fully fledged final solution. Here's some code to start with:import sys
import json
import os
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
import pandas as pd
import pdfplumber  # Added for PDF parsing
from PyQt6 import QtWidgets, QtGui, QtCore
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QTabWidget, QHBoxLayout,
    QLabel, QLineEdit, QPushButton, QMessageBox, QComboBox,
    QTextEdit, QListWidget, QListWidgetItem, QFileDialog, QSpinBox, QDialog, QTableWidget, QAbstractItemView
)
from PyQt6.QtCore import Qt
from openpyxl.utils.dataframe import dataframe_to_rows

# Define file paths
CLIENTS_FILE = 'clients.json'
LINE_ITEMS_FILE = 'line_items.json'
INVOICES_DIR = 'invoices'
EMAIL_CONFIG_FILE = 'email_config.json'
TEMPLATES_FILE = 'templates.json'
COMPANY_DETAILS_FILE = 'company_details.json'  # New file for company details
INVOICES_METADATA_FILE = 'invoices.json'  # New file for invoice metadata

# Ensure necessary directories exist
os.makedirs(INVOICES_DIR, exist_ok=True)

# Function to load data from JSON
def load_json(file_path):
    if os.path.exists(file_path):
        try:
            with open(file_path, 'r') as f:
                return json.load(f)
        except json.JSONDecodeError:
            return {}
    return {}

# Function to save data to JSON
def save_json(data, file_path):
    with open(file_path, 'w') as f:
        json.dump(data, f, indent=4)

# GUI Application
class QuickBooksReplacement(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.clients = load_json(CLIENTS_FILE)
        self.line_items_data = load_json(LINE_ITEMS_FILE)
        self.email_config = load_json(EMAIL_CONFIG_FILE)
        self.templates = load_json(TEMPLATES_FILE)
        self.company_details = load_json(COMPANY_DETAILS_FILE)  # Initialize company details
        self.invoices_metadata = load_json(INVOICES_METADATA_FILE)  # Initialize invoice metadata
        self.invoice_counter = self.get_next_invoice_number()  # Initialize invoice counter
        self.init_ui()

    def get_next_invoice_number(self):
        """Gets the next available invoice number."""
        if os.path.exists("invoice_counter.txt"):
            with open("invoice_counter.txt", "r") as f:
                try:
                    counter = int(f.read())
                    return counter + 1
                except ValueError:
                    return 1
        else:
            return 1

    def update_invoice_counter(self):
        """Updates the invoice counter file."""
        with open("invoice_counter.txt", "w") as f:
            f.write(str(self.invoice_counter))

    def init_ui(self):
        self.setWindowTitle("QuickBooks Replacement Tool")
        self.setGeometry(100, 100, 1400, 900)  # Increased width and height for better layout
        layout = QVBoxLayout()

        # Apply Enhanced Dark Theme
        self.apply_dark_theme()

        # Tab Widget
        self.tabs = QTabWidget()
        layout.addWidget(self.tabs)

        # Tabs
        self.client_tab = ClientManagementTab(self)
        self.line_item_tab = LineItemManagementTab(self)
        self.invoice_tab = InvoiceCreationTab(self)
        self.manage_invoices_tab = ManageInvoicesTab(self)
        self.financial_statements_tab = FinancialStatementsTab(self)
        self.email_config_tab = EmailConfigTab(self)
        self.company_details_tab = CompanyDetailsTab(self)
        self.templates_tab = TemplateManagementTab(self)

        self.tabs.addTab(self.client_tab, QtGui.QIcon.fromTheme("user-group"), "Clients")
        self.tabs.addTab(self.line_item_tab, QtGui.QIcon.fromTheme("list-add"), "Line Items")
        self.tabs.addTab(self.invoice_tab, QtGui.QIcon.fromTheme("document-new"), "Create Invoice")
        self.tabs.addTab(self.manage_invoices_tab, QtGui.QIcon.fromTheme("folder"), "Manage Invoices")
        self.tabs.addTab(self.financial_statements_tab, QtGui.QIcon.fromTheme("view-summary"), "Financial Statements")
        self.tabs.addTab(self.email_config_tab, QtGui.QIcon.fromTheme("email-send"), "Email Settings")
        self.tabs.addTab(self.company_details_tab, QtGui.QIcon.fromTheme("office-address"), "Company Details")
        self.tabs.addTab(self.templates_tab, QtGui.QIcon.fromTheme("format-paint"), "Templates")

        # Log Message Area
        self.log_area = QTextEdit(self)
        self.log_area.setReadOnly(True)
        self.log_area.setStyleSheet("""
            background-color: #1E1E1E;
            color: #ffffff;
            font-family: Consolas;
            font-size: 12px;
            border: 1px solid #555555;
        """)
        log_layout = QVBoxLayout()
        log_layout.addWidget(QLabel("Log:"))
        log_layout.addWidget(self.log_area)
        layout.addLayout(log_layout)

        self.setLayout(layout)

    def apply_dark_theme(self):
        dark_stylesheet = """
            /* General Widget Styles */
            QWidget {
                background-color: #121212;
                color: #ffffff;
                font-family: "Segoe UI", sans-serif;
                font-size: 14px;
            }
            QLabel {
                color: #ffffff;
            }
            QLineEdit, QTextEdit, QComboBox, QListWidget, QListWidgetItem, QSpinBox, QPushButton {
                background-color: #1E1E1E;
                border: 1px solid #555555;
                color: #ffffff;
                padding: 5px;
                border-radius: 4px;
            }
            QLineEdit:focus, QTextEdit:focus, QComboBox:focus, QListWidget:focus, QSpinBox:focus, QPushButton:focus {
                border: 2px solid #1E90FF;
            }
            QPushButton {
                background-color: #2D2D30;
            }
            QPushButton:hover {
                background-color: #3E3E42;
            }
            QPushButton:pressed {
                background-color: #5A5A5E;
            }
            QTabWidget::pane {
                border: 1px solid #555555;
            }
            QTabBar::tab {
                background: #2D2D30;
                border: 1px solid #555555;
                padding: 10px;
                margin: 2px;
                color: #ffffff;
                border-radius: 4px;
            }
            QTabBar::tab:selected {
                background: #1E90FF;
                color: #ffffff;
            }
            QMenuBar, QMenu {
                background-color: #121212;
                color: #ffffff;
                font-size: 14px;
            }
            QMenu::item:selected {
                background-color: #3E3E42;
            }
            QListWidget::item:selected {
                background-color: #1E90FF;
                color: #ffffff;
            }
            QHeaderView::section {
                background-color: #2D2D30;
                color: #ffffff;
                padding: 4px;
                border: 1px solid #555555;
            }
            QDialog {
                background-color: #121212;
                color: #ffffff;
            }
            QDialog QPushButton {
                min-width: 80px;
            }
        """
        self.setStyleSheet(dark_stylesheet)

    def log_message(self, message):
        """Logs message to the text area."""
        self.log_area.append(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}")

# Client Management Tab
class ClientManagementTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Form to add/edit client
        form_group = QtWidgets.QGroupBox("Add New Client")
        form_layout = QHBoxLayout()
        self.client_name_input = QLineEdit(self)
        self.client_name_input.setPlaceholderText("Client Name")
        self.client_email_input = QLineEdit(self)
        self.client_email_input.setPlaceholderText("Client Email")
        self.client_address_input = QLineEdit(self)
        self.client_address_input.setPlaceholderText("Client Address")
        self.client_vat_input = QLineEdit(self)  # VAT Number Field
        self.client_vat_input.setPlaceholderText("Client VAT Number")
        self.add_client_button = QPushButton("Add Client")
        self.add_client_button.clicked.connect(self.add_client)

        form_layout.addWidget(QLabel("Name:"))
        form_layout.addWidget(self.client_name_input)
        form_layout.addWidget(QLabel("Email:"))
        form_layout.addWidget(self.client_email_input)
        form_layout.addWidget(QLabel("Address:"))
        form_layout.addWidget(self.client_address_input)
        form_layout.addWidget(QLabel("VAT Number:"))  # Label for VAT
        form_layout.addWidget(self.client_vat_input)    # VAT Input
        form_layout.addWidget(self.add_client_button)

        form_group.setLayout(form_layout)
        layout.addWidget(form_group)

        # List of clients
        self.clients_list = QListWidget()
        self.load_clients()
        layout.addWidget(QLabel("Existing Clients:"))
        layout.addWidget(self.clients_list)

        # Buttons to edit and delete clients
        btn_layout = QHBoxLayout()
        self.edit_client_button = QPushButton("Edit Selected")
        self.edit_client_button.clicked.connect(self.edit_client)
        self.delete_client_button = QPushButton("Delete Selected")
        self.delete_client_button.clicked.connect(self.delete_client)
        btn_layout.addWidget(self.edit_client_button)
        btn_layout.addWidget(self.delete_client_button)
        layout.addLayout(btn_layout)

        self.setLayout(layout)

    def load_clients(self):
        self.clients_list.clear()
        for client in self.parent.clients.keys():
            item = QListWidgetItem(client)
            self.clients_list.addItem(item)

    def add_client(self):
        name = self.client_name_input.text().strip()
        email = self.client_email_input.text().strip()
        address = self.client_address_input.text().strip()
        vat = self.client_vat_input.text().strip()  # Retrieve VAT number

        if not name or not email or not address:
            QMessageBox.warning(self, "Input Error", "Please fill in all required fields.")
            return

        if name in self.parent.clients:
            QMessageBox.warning(self, "Duplicate Client", "Client already exists.")
            return

        self.parent.clients[name] = {
            "email": email,
            "address": address,
            "vat_number": vat  # Store VAT number
        }
        save_json(self.parent.clients, CLIENTS_FILE)
        self.parent.log_message(f"Client '{name}' added.")
        self.load_clients()
        self.client_name_input.clear()
        self.client_email_input.clear()
        self.client_address_input.clear()
        self.client_vat_input.clear()

    def edit_client(self):
        selected = self.clients_list.currentItem()
        if not selected:
            QMessageBox.warning(self, "Selection Error", "Please select a client to edit.")
            return
        name = selected.text()
        client_data = self.parent.clients[name]

        # Dialog to edit client
        dialog = EditClientDialog(name, client_data, self.parent)
        if dialog.exec():
            new_name, new_email, new_address, new_vat = dialog.get_data()
            if new_name != name and new_name in self.parent.clients:
                QMessageBox.warning(self, "Duplicate Client", "Client name already exists.")
                return
            # Update client
            del self.parent.clients[name]
            self.parent.clients[new_name] = {
                "email": new_email,
                "address": new_address,
                "vat_number": new_vat  # Update VAT number
            }
            save_json(self.parent.clients, CLIENTS_FILE)
            self.parent.log_message(f"Client '{name}' updated to '{new_name}'.")
            self.load_clients()

    def delete_client(self):
        selected = self.clients_list.currentItem()
        if not selected:
            QMessageBox.warning(self, "Selection Error", "Please select a client to delete.")
            return
        name = selected.text()
        reply = QMessageBox.question(
            self, 'Delete Confirmation',
            f"Are you sure you want to delete client '{name}'?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No
        )
        if reply == QMessageBox.StandardButton.Yes:
            del self.parent.clients[name]
            save_json(self.parent.clients, CLIENTS_FILE)
            self.parent.log_message(f"Client '{name}' deleted.")
            self.load_clients()

# Dialog for editing a client
class EditClientDialog(QDialog):
    def __init__(self, name, data, parent):
        super().__init__()
        self.setWindowTitle("Edit Client")
        self.parent = parent
        self.name = name
        self.data = data
        self.init_ui()

    def init_ui(self):
        self.layout = QVBoxLayout()

        self.name_input = QLineEdit(self)
        self.name_input.setText(self.name)
        self.email_input = QLineEdit(self)
        self.email_input.setText(self.data.get("email", ""))
        self.address_input = QLineEdit(self)
        self.address_input.setText(self.data.get("address", ""))
        self.vat_input = QLineEdit(self)  # VAT Number Field
        self.vat_input.setText(self.data.get("vat_number", ""))  # Retrieve VAT number

        self.layout.addWidget(QLabel("Name:"))
        self.layout.addWidget(self.name_input)
        self.layout.addWidget(QLabel("Email:"))
        self.layout.addWidget(self.email_input)
        self.layout.addWidget(QLabel("Address:"))
        self.layout.addWidget(self.address_input)
        self.layout.addWidget(QLabel("VAT Number:"))  # Label for VAT
        self.layout.addWidget(self.vat_input)          # VAT Input

        self.button_box = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.StandardButton.Ok | QtWidgets.QDialogButtonBox.StandardButton.Cancel
        )
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.layout.addWidget(self.button_box)

        self.setLayout(self.layout)

    def get_data(self):
        return (
            self.name_input.text().strip(),
            self.email_input.text().strip(),
            self.address_input.text().strip(),
            self.vat_input.text().strip()  # Return VAT number
        )

# Line Item Management Tab
class LineItemManagementTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Form to add/edit line item
        form_group = QtWidgets.QGroupBox("Add New Line Item")
        form_layout = QHBoxLayout()
        self.item_name_input = QLineEdit(self)
        self.item_name_input.setPlaceholderText("Item Name")
        self.item_price_input = QLineEdit(self)
        self.item_price_input.setPlaceholderText("Item Price")
        self.add_item_button = QPushButton("Add Item")
        self.add_item_button.clicked.connect(self.add_line_item)

        form_layout.addWidget(QLabel("Name:"))
        form_layout.addWidget(self.item_name_input)
        form_layout.addWidget(QLabel("Price:"))
        form_layout.addWidget(self.item_price_input)
        form_layout.addWidget(self.add_item_button)

        form_group.setLayout(form_layout)
        layout.addWidget(form_group)

        # Supplier Section (Optional)
        supplier_group = QtWidgets.QGroupBox("Supplier Information (Internal Use)")
        supplier_layout = QHBoxLayout()
        self.supplier_input = QLineEdit(self)
        self.supplier_input.setPlaceholderText("Supplier Name")
        supplier_layout.addWidget(QLabel("Supplier:"))
        supplier_layout.addWidget(self.supplier_input)
        supplier_group.setLayout(supplier_layout)
        layout.addWidget(supplier_group)

        # List of line items
        self.items_list = QListWidget()
        self.load_line_items()
        layout.addWidget(QLabel("Existing Line Items:"))
        layout.addWidget(self.items_list)

        # Buttons to edit, delete items
        btn_layout = QHBoxLayout()
        self.edit_item_button = QPushButton("Edit Selected")
        self.edit_item_button.clicked.connect(self.edit_line_item)
        self.delete_item_button = QPushButton("Delete Selected")
        self.delete_item_button.clicked.connect(self.delete_line_item)
        self.import_pdf_button = QPushButton("Import from PDF")  # New Import Button
        self.import_pdf_button.clicked.connect(self.import_from_pdf)
        btn_layout.addWidget(self.edit_item_button)
        btn_layout.addWidget(self.delete_item_button)
        btn_layout.addWidget(self.import_pdf_button)
        layout.addLayout(btn_layout)

        self.setLayout(layout)

    def load_line_items(self):
        self.items_list.clear()
        for item, details in self.parent.line_items_data.items():
            price = details.get('price', 0.0)
            self.items_list.addItem(f"{item} - Price: R{price:.2f}")

    def add_line_item(self):
        name = self.item_name_input.text().strip()
        price = self.item_price_input.text().strip()
        supplier = self.supplier_input.text().strip()  # Optional, for internal use

        if not name or not price:
            QMessageBox.warning(self, "Input Error", "Please fill in the Item Name and Price.")
            return

        try:
            price = float(price)
            if price < 0:
                raise ValueError
        except ValueError:
            QMessageBox.warning(self, "Input Error", "Please enter a valid positive price.")
            return

        if name in self.parent.line_items_data:
            QMessageBox.warning(self, "Duplicate Item", "Line item already exists.")
            return

        self.parent.line_items_data[name] = {"price": price, "supplier": supplier}
        save_json(self.parent.line_items_data, LINE_ITEMS_FILE)
        self.parent.log_message(f"Line item '{name}' added.")
        self.load_line_items()
        self.item_name_input.clear()
        self.item_price_input.clear()
        self.supplier_input.clear()

    def edit_line_item(self):
        selected = self.items_list.currentItem()
        if not selected:
            QMessageBox.warning(self, "Selection Error", "Please select a line item to edit.")
            return
        item_text = selected.text()
        name = item_text.split(" - Price: ")[0]
        item_data = self.parent.line_items_data[name]

        # Dialog to edit line item
        dialog = EditLineItemDialog(name, item_data, self.parent)
        if dialog.exec():
            new_name, new_price, new_supplier = dialog.get_data()
            if new_name != name and new_name in self.parent.line_items_data:
                QMessageBox.warning(self, "Duplicate Item", "Line item name already exists.")
                return
            try:
                new_price = float(new_price)
                if new_price < 0:
                    raise ValueError
            except ValueError:
                QMessageBox.warning(self, "Input Error", "Please enter a valid positive price.")
                return
            # Update line item
            del self.parent.line_items_data[name]
            self.parent.line_items_data[new_name] = {"price": new_price, "supplier": new_supplier}
            save_json(self.parent.line_items_data, LINE_ITEMS_FILE)
            self.parent.log_message(f"Line item '{name}' updated to '{new_name}'.")
            self.load_line_items()

    def delete_line_item(self):
        selected = self.items_list.currentItem()
        if not selected:
            QMessageBox.warning(self, "Selection Error", "Please select a line item to delete.")
            return
        item_text = selected.text()
        name = item_text.split(" - Price: ")[0]
        reply = QMessageBox.question(
            self, 'Delete Confirmation',
            f"Are you sure you want to delete line item '{name}'?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No
        )
        if reply == QMessageBox.StandardButton.Yes:
            del self.parent.line_items_data[name]
            save_json(self.parent.line_items_data, LINE_ITEMS_FILE)
            self.parent.log_message(f"Line item '{name}' deleted.")
            self.load_line_items()

    def import_from_pdf(self):
        """Imports line items from a selected PDF file."""
        file_path, _ = QFileDialog.getOpenFileName(self, "Select PDF", "", "PDF Files (*.pdf)")
        if not file_path:
            return

        try:
            with pdfplumber.open(file_path) as pdf:
                imported_items = 0
                for page in pdf.pages:
                    tables = page.extract_tables()
                    for table in tables:
                        if not table:
                            continue
                        headers = table[0]
                        if headers and any("Item" in header for header in headers) and \
                           any("Quantity" in header for header in headers) and \
                           any("Price" in header for header in headers) and \
                           any("Total" in header for header in headers):
                            # Determine the indices of each required column
                            item_idx = next((i for i, header in enumerate(headers) if "Item" in header), None)
                            quantity_idx = next((i for i, header in enumerate(headers) if "Quantity" in header), None)
                            price_idx = next((i for i, header in enumerate(headers) if "Price" in header), None)
                            total_idx = next((i for i, header in enumerate(headers) if "Total" in header), None)

                            if None in (item_idx, quantity_idx, price_idx, total_idx):
                                continue  # Missing required columns

                            for row in table[1:]:
                                if len(row) < 4:
                                    continue
                                item_name = row[item_idx].strip()
                                quantity = row[quantity_idx].strip()
                                price = row[price_idx].strip().replace('R', '').replace(',', '')
                                total = row[total_idx].strip().replace('R', '').replace(',', '')

                                # Validate and convert data
                                if not item_name:
                                    continue
                                try:
                                    quantity = int(quantity)
                                    price = float(price)
                                    total = float(total)
                                except ValueError:
                                    self.parent.log_message(f"Invalid data in PDF for item '{item_name}'. Skipping.")
                                    continue

                                # Add to line items
                                if item_name not in self.parent.line_items_data:
                                    self.parent.line_items_data[item_name] = {
                                        "price": price,
                                        "supplier": ""  # Supplier not included as per requirement
                                    }
                                    imported_items += 1
                                else:
                                    self.parent.log_message(f"Line item '{item_name}' already exists. Skipping import.")

                if imported_items > 0:
                    save_json(self.parent.line_items_data, LINE_ITEMS_FILE)
                    self.parent.log_message(f"Imported {imported_items} line items from PDF.")
                    QMessageBox.information(self, "Import Successful", f"Imported {imported_items} line items from the PDF.")
                    self.load_line_items()
                else:
                    QMessageBox.information(self, "Import Result", "No new line items were imported from the PDF.")

        except Exception as e:
            QMessageBox.warning(self, "Import Error", f"Failed to import from PDF: {e}")
            self.parent.log_message(f"Failed to import from PDF '{file_path}': {e}")

# Dialog for editing a line item
class EditLineItemDialog(QDialog):
    def __init__(self, name, data, parent):
        super().__init__()
        self.setWindowTitle("Edit Line Item")
        self.parent = parent
        self.name = name
        self.data = data
        self.init_ui()

    def init_ui(self):
        self.layout = QVBoxLayout()

        self.name_input = QLineEdit(self)
        self.name_input.setText(self.name)
        self.price_input = QLineEdit(self)
        self.price_input.setText(str(self.data["price"]))
        self.supplier_input = QLineEdit(self)
        self.supplier_input.setText(self.data.get("supplier", ""))  # Supplier Input

        self.layout.addWidget(QLabel("Name:"))
        self.layout.addWidget(self.name_input)
        self.layout.addWidget(QLabel("Price:"))
        self.layout.addWidget(self.price_input)
        self.layout.addWidget(QLabel("Supplier:"))  # Supplier Label
        self.layout.addWidget(self.supplier_input)   # Supplier Input

        self.button_box = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.StandardButton.Ok | QtWidgets.QDialogButtonBox.StandardButton.Cancel
        )
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.layout.addWidget(self.button_box)

        self.setLayout(self.layout)

    def get_data(self):
        return (
            self.name_input.text().strip(),
            self.price_input.text().strip(),
            self.supplier_input.text().strip()  # Return Supplier
        )

# Invoice Creation Tab
class InvoiceCreationTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.line_items = []
        self.selected_template = None  # For template usage
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Template Selection
        template_layout = QHBoxLayout()
        self.template_combo = QComboBox(self)
        self.load_templates()
        self.template_combo.currentIndexChanged.connect(self.change_template)
        template_layout.addWidget(QLabel("Select Template:"))
        template_layout.addWidget(self.template_combo)
        layout.addLayout(template_layout)

        # Client selection
        client_layout = QHBoxLayout()
        self.client_combo = QComboBox(self)
        self.load_clients()
        client_layout.addWidget(QLabel("Select Client:"))
        client_layout.addWidget(self.client_combo)
        layout.addLayout(client_layout)

        # VAT Number Display (Read-only)
        vat_display_layout = QHBoxLayout()
        self.vat_display = QLineEdit(self)
        self.vat_display.setReadOnly(True)
        vat_display_layout.addWidget(QLabel("Client VAT Number:"))
        vat_display_layout.addWidget(self.vat_display)
        layout.addLayout(vat_display_layout)

        # Update VAT display when client changes
        self.client_combo.currentIndexChanged.connect(self.update_vat_display)
        self.update_vat_display()  # Initialize VAT display

        # Line Items selection
        line_item_layout = QHBoxLayout()
        self.item_combo = QComboBox(self)
        self.load_line_items()
        self.quantity_input = QSpinBox(self)
        self.quantity_input.setMinimum(1)
        self.quantity_input.setValue(1)
        self.add_item_button = QPushButton("Add Item")
        self.add_item_button.clicked.connect(self.add_line_item)
        line_item_layout.addWidget(QLabel("Item:"))
        line_item_layout.addWidget(self.item_combo)
        line_item_layout.addWidget(QLabel("Quantity:"))
        line_item_layout.addWidget(self.quantity_input)
        line_item_layout.addWidget(self.add_item_button)
        layout.addLayout(line_item_layout)

        # List to display added line items without supplier info
        self.added_items_list = QListWidget()
        self.added_items_list.setStyleSheet("""
            QListWidget {
                background-color: #1E1E1E;
                color: #ffffff;
                border: 1px solid #555555;
            }
            QListWidget::item {
                padding: 5px;
            }
        """)
        layout.addWidget(QLabel("Added Line Items:"))
        layout.addWidget(self.added_items_list)

        # Discounts and Taxes
        discounts_taxes_layout = QHBoxLayout()
        self.discount_input = QLineEdit(self)
        self.discount_input.setPlaceholderText("Discount (%)")
        self.tax_rate_input = QLineEdit(self)
        self.tax_rate_input.setPlaceholderText("Tax Rate (%)")
        discounts_taxes_layout.addWidget(QLabel("Discount (%):"))
        discounts_taxes_layout.addWidget(self.discount_input)
        discounts_taxes_layout.addWidget(QLabel("Tax Rate (%):"))
        discounts_taxes_layout.addWidget(self.tax_rate_input)
        layout.addLayout(discounts_taxes_layout)

        # Output Format Selection
        output_layout = QHBoxLayout()
        self.output_format_combo = QComboBox(self)
        self.output_format_combo.addItems(["PDF", "Excel"])
        output_layout.addWidget(QLabel("Output Format:"))
        output_layout.addWidget(self.output_format_combo)
        layout.addLayout(output_layout)

        # Button to create invoice
        self.create_invoice_button = QPushButton("Create Invoice")
        self.create_invoice_button.clicked.connect(self.create_invoice)
        layout.addWidget(self.create_invoice_button)

        self.setLayout(layout)

    def load_templates(self):
        self.template_combo.clear()
        templates_list = list(self.parent.templates.keys())
        self.template_combo.addItems(["Default"] + templates_list)
        self.selected_template = "Default"

    def change_template(self, index):
        selected = self.template_combo.currentText()
        if selected == "Default":
            self.selected_template = None
        else:
            self.selected_template = selected

    def load_clients(self):
        self.client_combo.clear()
        self.client_combo.addItems(list(self.parent.clients.keys()))

    def load_line_items(self):
        self.item_combo.clear()
        self.item_combo.addItems(list(self.parent.line_items_data.keys()))

    def add_line_item(self):
        item_name = self.item_combo.currentText()
        quantity = self.quantity_input.value()
        if not item_name:
            QMessageBox.warning(self, "Selection Error", "Please select a line item.")
            return
        price = self.parent.line_items_data[item_name]['price']
        total = price * quantity
        self.line_items.append({
            "item": item_name,
            "quantity": quantity,
            "price": price,
            "total": total
        })
        self.update_added_items_list()

    def update_added_items_list(self):
        self.added_items_list.clear()
        for idx, item in enumerate(self.line_items, 1):
            self.added_items_list.addItem(
                f"{idx}. {item['item']} - Qty: {item['quantity']} - Price: R{item['price']:.2f} - Total: R{item['total']:.2f}"
            )

    def update_vat_display(self):
        client_name = self.client_combo.currentText()
        vat_number = self.parent.clients.get(client_name, {}).get("vat_number", "")
        self.vat_display.setText(vat_number)

    def create_invoice(self):
        client_name = self.client_combo.currentText()
        vat_number = self.parent.clients.get(client_name, {}).get("vat_number", "")
        discount = self.discount_input.text().strip()
        tax_rate = self.tax_rate_input.text().strip()

        if not client_name:
            QMessageBox.warning(self, "Input Error", "Please select a client.")
            return

        if not self.line_items:
            QMessageBox.warning(self, "Input Error", "Please add at least one line item.")
            return

        # Calculate totals
        subtotal = sum(item['total'] for item in self.line_items)
        discount_amount = 0
        if discount:
            try:
                discount_percentage = float(discount)
                discount_amount = subtotal * (discount_percentage / 100)
            except ValueError:
                QMessageBox.warning(self, "Input Error", "Please enter a valid discount percentage.")
                return

        subtotal_after_discount = subtotal - discount_amount

        tax_amount = 0
        if tax_rate:
            try:
                tax_percentage = float(tax_rate)
                tax_amount = subtotal_after_discount * (tax_percentage / 100)
            except ValueError:
                QMessageBox.warning(self, "Input Error", "Please enter a valid tax rate percentage.")
                return

        total = subtotal_after_discount + tax_amount

        invoice_date = datetime.now().strftime("%Y-%m-%d")  # Automatic date filling
        client_address = self.parent.clients[client_name]["address"]
        invoice_filename = f"{client_name.replace(' ', '_')}_invoice_{self.parent.invoice_counter}"

        # Prepare DataFrame without supplier info
        df = pd.DataFrame([
            {"Item": item['item'], "Quantity": item['quantity'], "Price ": item['price'], "Total ": item['total']}
            for item in self.line_items
        ])

        output_format = self.output_format_combo.currentText()
        success = False
        if output_format == "PDF":
            success = self.create_pdf(client_name, vat_number, invoice_date, client_address, df, invoice_filename, subtotal, discount_amount, tax_amount, total)
        elif output_format == "Excel":
            success = self.create_excel(client_name, vat_number, invoice_date, client_address, df, invoice_filename, subtotal, discount_amount, tax_amount, total)
        else:
            QMessageBox.warning(self, "Format Error", "Please select a valid output format.")
            return

        if success:
            # Log invoice metadata
            invoice_number = self.parent.invoice_counter - 1
            invoice_metadata = {
                "invoice_number": invoice_number,
                "client_name": client_name,
                "date": invoice_date,
                "total": total,
                "status": "Unpaid",
                "filename": f"{invoice_filename}.{output_format.lower()}"
            }
            self.parent.invoices_metadata[str(invoice_number)] = invoice_metadata
            save_json(self.parent.invoices_metadata, INVOICES_METADATA_FILE)

            # Reset form
            self.line_items = []
            self.update_added_items_list()
            self.parent.invoice_counter += 1  # Increment invoice counter after successful creation
            self.parent.update_invoice_counter()

    def create_pdf(self, client, vat, invoice_date, client_address, df, invoice_filename, subtotal, discount, tax, total):
        """Generate PDF invoice."""
        try:
            pdf_filename = os.path.join(INVOICES_DIR, f"{invoice_filename}.pdf")
            template = self.get_selected_template()

            c = canvas.Canvas(pdf_filename, pagesize=letter)
            width, height = letter

            # Apply Template Attributes
            invoice_details = {'width': width, 'height': height}
            self.apply_template_attributes(c, template, invoice_details)

            # Company Details
            company_name = self.parent.company_details.get("company_name", "Your Company Name")
            company_address = self.parent.company_details.get("company_address", "Your Company Address")
            company_email = self.parent.company_details.get("company_email", "your.email@example.com")
            company_phone = self.parent.company_details.get("company_phone", "123-456-7890")
            # Optional: Company logo
            logo_path = self.parent.company_details.get("company_logo_path", "")
            if logo_path and os.path.exists(logo_path):
                c.drawImage(logo_path, 50, height - 100, width=100, height=50)

            # Improved Company Details Layout with box
            c.setLineWidth(1)
            c.rect(40, height - 200, 520, 100, stroke=1, fill=0)

            c.setFont("Helvetica-Bold", 14)
            c.drawString(50, height - 120, company_name)
            c.setFont("Helvetica", 12)
            c.drawString(50, height - 140, company_address)
            c.drawString(50, height - 160, f"Email: {company_email}")
            c.drawString(50, height - 180, f"Phone: {company_phone}")

            # Invoice Header with box
            c.setFont("Helvetica-Bold", 20)
            c.drawString(400, height - 120, "INVOICE")
            c.rect(390, height - 130, 150, 30, stroke=1, fill=0)  # Box around "INVOICE"

            # Invoice Information with box
            c.setFont("Helvetica", 12)
            c.drawString(400, height - 150, f"Invoice Number: {self.parent.invoice_counter - 1}")  # Displaying counter
            c.drawString(400, height - 170, f"Date: {invoice_date}")
            c.rect(390, height - 190, 150, 50, stroke=1, fill=0)  # Box around invoice info

            # Client Information with box
            c.setFont("Helvetica-Bold", 12)
            c.drawString(50, height - 220, "Bill To:")
            c.setFont("Helvetica", 12)
            c.drawString(50, height - 240, f"{client}")
            c.drawString(50, height - 260, f"Address: {client_address}")
            if vat:
                c.drawString(50, height - 280, f"VAT Number: {vat}")
            c.rect(40, height - 300, 520, 80, stroke=1, fill=0)  # Box around client info

            # Table Headers with background color
            c.setFillColorRGB(0.8, 0.8, 0.8)
            c.rect(50, height - 350, 500, 20, fill=1, stroke=0)
            c.setFillColorRGB(0, 0, 0)
            c.setFont("Helvetica-Bold", 12)
            y = height - 340
            c.drawString(55, y, "Item")
            c.drawString(255, y, "Quantity")
            c.drawString(355, y, "Price ")
            c.drawString(455, y, "Total ")
            y -= 20
            c.line(50, y + 5, 550, y + 5)

            # Table Content
            c.setFont("Helvetica", 12)
            c.setFillColorRGB(0, 0, 0)
            for index, row in df.iterrows():
                y -= 20
                if y < 150:
                    c.showPage()
                    y = height - 50
                    # Re-apply template on new page
                    self.apply_template_attributes(c, template, invoice_details)
                    # Re-draw company details
                    c.setFont("Helvetica-Bold", 14)
                    c.drawString(50, height - 120, company_name)
                    c.setFont("Helvetica", 12)
                    c.drawString(50, height - 140, company_address)
                    c.drawString(50, height - 160, f"Email: {company_email}")
                    c.drawString(50, height - 180, f"Phone: {company_phone}")
                    # Re-draw invoice header
                    c.setFont("Helvetica-Bold", 20)
                    c.drawString(400, height - 120, "INVOICE")
                    c.rect(390, height - 130, 150, 30, stroke=1, fill=0)
                    # Re-draw invoice information
                    c.setFont("Helvetica", 12)
                    c.drawString(400, height - 150, f"Invoice Number: {self.parent.invoice_counter - 1}")
                    c.drawString(400, height - 170, f"Date: {invoice_date}")
                    c.rect(390, height - 190, 150, 50, stroke=1, fill=0)
                    # Re-draw client information
                    c.setFont("Helvetica-Bold", 12)
                    c.drawString(50, height - 220, "Bill To:")
                    c.setFont("Helvetica", 12)
                    c.drawString(50, height - 240, f"{client}")
                    c.drawString(50, height - 260, f"Address: {client_address}")
                    if vat:
                        c.drawString(50, height - 280, f"VAT Number: {vat}")
                    c.rect(40, height - 300, 520, 80, stroke=1, fill=0)
                    # Re-draw table headers
                    c.setFillColorRGB(0.8, 0.8, 0.8)
                    c.rect(50, y + 20, 500, 20, fill=1, stroke=0)
                    c.setFillColorRGB(0, 0, 0)
                    c.setFont("Helvetica-Bold", 12)
                    c.drawString(55, y + 25, "Item")
                    c.drawString(255, y + 25, "Quantity")
                    c.drawString(355, y + 25, "Price ")
                    c.drawString(455, y + 25, "Total ")
                    y -= 20
                    c.line(50, y + 25, 550, y + 25)
                    c.setFont("Helvetica", 12)
                    c.setFillColorRGB(0, 0, 0)

                c.setFillColorRGB(0, 0, 0)
                c.drawString(55, y, str(row['Item']))
                c.drawString(255, y, str(row['Quantity']))
                c.drawString(355, y, f"R{row['Price ']:.2f}")
                c.drawString(455, y, f"R{row['Total ']:.2f}")
                y -= 5
                # Draw lines around each row
                c.setLineWidth(0.5)
                c.line(50, y, 550, y)
                c.setLineWidth(1)

            # Totals with box
            y -= 40
            c.setFont("Helvetica-Bold", 12)
            c.drawString(355, y, "Subtotal:")
            c.drawString(455, y, f"R{subtotal:.2f}")
            y -= 20
            if discount > 0:
                c.drawString(355, y, "Discount:")
                c.drawString(455, y, f"R{-discount:.2f}")
                y -= 20
            if tax > 0:
                c.drawString(355, y, "Tax:")
                c.drawString(455, y, f"R{tax:.2f}")
                y -= 20
            c.drawString(355, y, "Total:")
            c.drawString(455, y, f"R{total:.2f}")
            # Draw a box around the totals
            c.rect(340, y - 10, 220, 80, stroke=1, fill=0)

            # Footer with styling
            c.setFont("Helvetica-Oblique", 12)
            c.drawString(50, 30, "Thank you for your business!")  # Enhanced footnote

            c.save()
            self.parent.log_message(f"Invoice saved as {pdf_filename}")
            self.parent.manage_invoices_tab.load_invoices()
            self.parent.financial_statements_tab.load_financial_statements()  # Refresh financial statements
            self.send_email_invoice(client, pdf_filename)
            return True
        except Exception as e:
            QMessageBox.warning(self, "PDF Generation Error", f"Failed to generate PDF invoice: {e}")
            self.parent.log_message(f"Failed to generate PDF invoice '{invoice_filename}': {e}")
            return False

    def create_excel(self, client, vat, invoice_date, client_address, df, invoice_filename, subtotal, discount, tax, total):
        """Generate Excel invoice."""
        try:
            excel_filename = os.path.join(INVOICES_DIR, f"{invoice_filename}.xlsx")
            template = self.get_selected_template()

            writer = pd.ExcelWriter(excel_filename, engine='openpyxl')
            df.to_excel(writer, index=False, sheet_name='Invoice')
            workbook = writer.book
            worksheet = writer.sheets['Invoice']

            # Apply Template Attributes (basic - headers)
            current_row = 1
            if 'header_text' in template:
                worksheet.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=5)
                cell = worksheet.cell(row=current_row, column=1)
                cell.value = template['header_text']
                cell.font = cell.font.copy(bold=True, size=16)
                current_row += 1

            # Company Details
            company_name = self.parent.company_details.get("company_name", "Your Company Name")
            company_address = self.parent.company_details.get("company_address", "Your Company Address")
            company_email = self.parent.company_details.get("company_email", "your.email@example.com")
            company_phone = self.parent.company_details.get("company_phone", "123-456-7890")
            company_VAT = self.parent.company_details.get("company_VAT", "123-456-7890")
            company_logo = self.parent.company_details.get("company_logo_path", "")

            if company_logo and os.path.exists(company_logo):
                # Insert company logo if available
                from openpyxl.drawing.image import Image as XLImage
                img_excel = XLImage(company_logo)
                img_excel.anchor = 'A1'
                worksheet.add_image(img_excel)
                current_row += 5  # Adjust row if logo is added

            worksheet.cell(row=current_row, column=1, value=company_name)
            worksheet.cell(row=current_row, column=2, value=f"Invoice Number: {self.parent.invoice_counter - 1}")
            current_row += 1
            worksheet.cell(row=current_row, column=1, value=company_address)
            worksheet.cell(row=current_row, column=2, value=f"Date: {invoice_date}")
            current_row += 1
            worksheet.cell(row=current_row, column=1, value=f"Email: {company_email}")
            worksheet.cell(row[current_row, 2], f"Client: {client}")
            current_row += 1
            worksheet.cell(row=current_row, column=1, value=f"Phone: {company_phone}")
            worksheet.cell(row[current_row, 2], f"Address: {client_address}")
            current_row += 1
            if vat:
                worksheet.cell(row=current_row, column=1, value=f"VAT Number: {vat}")
                current_row += 1

            # Shift DataFrame down to make space for headers
            df_start_row = current_row + 2
            for idx, row in df.iterrows():
                worksheet.cell(row=df_start_row + idx, column=1, value=row['Item'])
                worksheet.cell(row=df_start_row + idx, column=2, value=row['Quantity'])
                worksheet.cell(row=df_start_row + idx, column=3, value=row['Price '])
                worksheet.cell(row=df_start_row + idx, column=4, value=row['Total '])

            # Add totals
            current_row = df_start_row + len(df) + 2
            worksheet.cell(row=current_row, column=3, value="Subtotal:")
            worksheet.cell(row[current_row, 4], subtotal)
            if discount > 0:
                current_row += 1
                worksheet.cell(row=current_row, column=3, value="Discount:")
                worksheet.cell(row[current_row, 4], -discount)
            if tax > 0:
                current_row += 1
                worksheet.cell(row=current_row, column=3, value="Tax:")
                worksheet.cell(row[current_row, 4], tax)
            current_row += 1
            worksheet.cell(row=current_row, column=3, value="Total:")
            worksheet.cell(row[current_row, 4], total)

            # Add footnote
            current_row += 2
            worksheet.cell(row=current_row, column=1, value="Thank you for your business.")

            writer.save()
            self.parent.log_message(f"Invoice saved as {excel_filename}")
            self.parent.manage_invoices_tab.load_invoices()
            self.parent.financial_statements_tab.load_financial_statements()  # Refresh financial statements
            self.send_email_invoice(client, excel_filename)
            return True
        except Exception as e:
            QMessageBox.warning(self, "Excel Generation Error", f"Failed to generate Excel invoice: {e}")
            self.parent.log_message(f"Failed to generate Excel invoice '{invoice_filename}': {e}")
            return False

    def get_selected_template(self):
        """Retrieves the selected template's attributes."""
        if self.selected_template and self.selected_template in self.parent.templates:
            return self.parent.templates[self.selected_template]
        return {}

    def apply_template_attributes(self, c, template, invoice_details):
        """Applies template attributes like header, footer, and logo to the PDF."""
        if 'header_text' in template:
            c.setFont("Helvetica-Bold", 16)
            c.drawString(50, invoice_details['height'] - 50, template['header_text'])

        if 'footer_text' in template:
            c.setFont("Helvetica", 10)
            c.drawString(50, 30, template['footer_text'])  # Adjusted position for footer

        if 'logo_path' in template and template['logo_path']:
            if os.path.exists(template['logo_path']):
                c.drawImage(template['logo_path'], invoice_details['width'] - 150, invoice_details['height'] - 100, width=100, height=50)

    def send_email_invoice(self, client_name, invoice_path):
        """Sends the invoice to the client's email."""
        try:
            client_email = self.parent.clients[client_name]["email"]
            subject = f"Invoice from {self.parent.company_details.get('company_name', 'Your Company')}"
            body = f"Dear {client_name},\n\nPlease find attached your invoice.\n\nBest regards,\n{self.parent.company_details.get('company_name', 'Your Company')}"

            sender_email = self.parent.email_config.get("sender_email")
            sender_password = self.parent.email_config.get("sender_password")
            smtp_server = self.parent.email_config.get("smtp_server")
            smtp_port = self.parent.email_config.get("smtp_port")

            if not sender_email or not sender_password or not smtp_server or not smtp_port:
                QMessageBox.warning(self, "Email Configuration Error", "Please configure email settings before sending invoices.")
                return

            msg = MIMEMultipart()
            msg['From'] = sender_email
            msg['To'] = client_email
            msg['Subject'] = subject
            msg.attach(MIMEText(body, 'plain'))

            with open(invoice_path, "rb") as f:
                part = MIMEApplication(f.read(), Name=os.path.basename(invoice_path))
            part['Content-Disposition'] = f'attachment; filename="{os.path.basename(invoice_path)}"'
            msg.attach(part)

            server = smtplib.SMTP(smtp_server, int(smtp_port))
            server.starttls()
            server.login(sender_email, sender_password)
            server.send_message(msg)
            server.quit()

            self.parent.log_message(f"Invoice emailed to {client_email}")
        except Exception as e:
            QMessageBox.warning(self, "Email Sending Error", f"Failed to send invoice via email: {e}")
            self.parent.log_message(f"Failed to send invoice to '{client_name}' via email: {e}")

# Manage Invoices Tab
class ManageInvoicesTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # List of invoices
        self.invoices_list = QListWidget()
        self.invoices_list.setStyleSheet("""
            QListWidget {
                background-color: #1E1E1E;
                color: #ffffff;
                border: 1px solid #555555;
            }
            QListWidget::item {
                padding: 5px;
            }
        """)
        self.load_invoices()
        layout.addWidget(QLabel("Invoices:"))
        layout.addWidget(self.invoices_list)

        # Buttons to view, resend, delete invoices
        btn_layout = QHBoxLayout()
        self.view_invoice_button = QPushButton("View Invoice")
        self.view_invoice_button.clicked.connect(self.view_invoice)
        self.resend_invoice_button = QPushButton("Resend Invoice")
        self.resend_invoice_button.clicked.connect(self.resend_invoice)
        self.delete_invoice_button = QPushButton("Delete Invoice")
        self.delete_invoice_button.clicked.connect(self.delete_invoice)
        btn_layout.addWidget(self.view_invoice_button)
        btn_layout.addWidget(self.resend_invoice_button)
        btn_layout.addWidget(self.delete_invoice_button)
        layout.addLayout(btn_layout)

        self.setLayout(layout)

    def load_invoices(self):
        self.invoices_list.clear()
        if not os.path.exists(INVOICES_DIR):
            return
        invoices = sorted(os.listdir(INVOICES_DIR), reverse=True)
        for invoice in invoices:
            item = QListWidgetItem(invoice)
            self.invoices_list.addItem(item)

    def get_selected_invoice_path(self):
        selected = self.invoices_list.currentItem()
        if not selected:
            QMessageBox.warning(self, "Selection Error", "Please select an invoice.")
            return None
        invoice_filename = selected.text()
        invoice_path = os.path.join(INVOICES_DIR, invoice_filename)
        return invoice_path

    def view_invoice(self):
        invoice_path = self.get_selected_invoice_path()
        if not invoice_path:
            return
        if not os.path.exists(invoice_path):
            QMessageBox.warning(self, "File Not Found", "The selected invoice file does not exist.")
            return
        QtGui.QDesktopServices.openUrl(QtCore.QUrl.fromLocalFile(os.path.abspath(invoice_path)))

    def resend_invoice(self):
        invoice_path = self.get_selected_invoice_path()
        if not invoice_path:
            return
        try:
            invoice_filename = os.path.basename(invoice_path)
            parts = invoice_filename.split('_invoice_')
            if len(parts) < 2:
                raise ValueError("Invalid invoice filename format.")
            invoice_number_part = parts[1].split('.')[0]
            invoice_number = str(int(invoice_number_part))
            invoice_metadata = self.parent.invoices_metadata.get(invoice_number, {})
            if not invoice_metadata:
                raise ValueError("Invoice metadata not found.")
            client_name = invoice_metadata["client_name"]
            if client_name not in self.parent.clients:
                raise ValueError("Client not found.")
            self.parent.invoice_tab.send_email_invoice(client_name, invoice_path)
        except Exception as e:
            QMessageBox.warning(self, "Resend Error", f"Failed to resend invoice: {e}")
            self.parent.log_message(f"Failed to resend invoice '{invoice_path}': {e}")

    def delete_invoice(self):
        invoice_path = self.get_selected_invoice_path()
        if not invoice_path:
            return
        reply = QMessageBox.question(
            self, 'Delete Confirmation',
            f"Are you sure you want to delete invoice '{os.path.basename(invoice_path)}'?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No
        )
        if reply == QMessageBox.StandardButton.Yes:
            try:
                invoice_filename = os.path.basename(invoice_path)
                parts = invoice_filename.split('_invoice_')
                if len(parts) < 2:
                    raise ValueError("Invalid invoice filename format.")
                invoice_number_part = parts[1].split('.')[0]
                invoice_number = str(int(invoice_number_part))
                if invoice_number in self.parent.invoices_metadata:
                    del self.parent.invoices_metadata[invoice_number]
                    save_json(self.parent.invoices_metadata, INVOICES_METADATA_FILE)

                os.remove(invoice_path)
                self.parent.log_message(f"Invoice '{os.path.basename(invoice_path)}' deleted.")
                self.load_invoices()
                self.parent.financial_statements_tab.load_financial_statements()  # Refresh financial statements
            except Exception as e:
                QMessageBox.warning(self, "Delete Error", f"Failed to delete invoice: {e}")
                self.parent.log_message(f"Failed to delete invoice '{invoice_path}': {e}")

# Financial Statements Tab
class FinancialStatementsTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Client Selection
        client_layout = QHBoxLayout()
        self.client_combo = QComboBox(self)
        self.load_clients()
        client_layout.addWidget(QLabel("Select Client:"))
        client_layout.addWidget(self.client_combo)
        layout.addLayout(client_layout)

        # Financial Statements Table
        self.financial_table = QTableWidget()
        self.financial_table.setColumnCount(5)
        self.financial_table.setHorizontalHeaderLabels(["Invoice Number", "Date", "Total ", "Status", "Filename"])
        self.financial_table.horizontalHeader().setStretchLastSection(True)
        self.financial_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.financial_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        layout.addWidget(QLabel("Financial Statements:"))
        layout.addWidget(self.financial_table)

        # Button to mark as paid
        btn_layout = QHBoxLayout()
        self.mark_paid_button = QPushButton("Mark as Paid")
        self.mark_paid_button.clicked.connect(self.mark_as_paid)
        btn_layout.addWidget(self.mark_paid_button)
        layout.addLayout(btn_layout)

        # Totals Display
        totals_layout = QHBoxLayout()
        self.total_invoiced_label = QLabel("Total Invoiced: R0.00")
        self.total_paid_label = QLabel("Total Paid: R0.00")  # Placeholder, can be implemented if payment tracking exists
        self.balance_label = QLabel("Balance: R0.00")
        totals_layout.addWidget(self.total_invoiced_label)
        totals_layout.addWidget(self.total_paid_label)
        totals_layout.addWidget(self.balance_label)
        layout.addLayout(totals_layout)

        # Load financial statements when client is selected
        self.client_combo.currentIndexChanged.connect(self.load_financial_statements)
        self.load_financial_statements()  # Initialize

        self.setLayout(layout)

    def load_clients(self):
        self.client_combo.clear()
        self.client_combo.addItems(list(self.parent.clients.keys()))

    def load_financial_statements(self):
        client_name = self.client_combo.currentText()
        if not client_name:
            return

        # Clear existing table
        self.financial_table.setRowCount(0)
        total_invoiced = 0.0
        total_paid = 0.0  # Placeholder for paid amounts
        balance = 0.0

        # Iterate through invoice metadata and populate table
        for invoice_number, metadata in self.parent.invoices_metadata.items():
            if metadata["client_name"] != client_name:
                continue
            invoice_number_display = metadata.get("invoice_number", "N/A")
            invoice_date = metadata.get("date", "N/A")
            total = metadata.get("total", 0.0)
            status = metadata.get("status", "Unpaid")
            filename = metadata.get("filename", "N/A")

            total_invoiced += total
            if status.lower() == "paid":
                total_paid += total
            balance += total if status.lower() != "paid" else 0.0

            # Add row to table
            row_position = self.financial_table.rowCount()
            self.financial_table.insertRow(row_position)
            self.financial_table.setItem(row_position, 0, QtWidgets.QTableWidgetItem(str(invoice_number_display)))
            self.financial_table.setItem(row_position, 1, QtWidgets.QTableWidgetItem(invoice_date))
            self.financial_table.setItem(row_position, 2, QtWidgets.QTableWidgetItem(f"R{total:.2f}"))
            self.financial_table.setItem(row_position, 3, QtWidgets.QTableWidgetItem(status))
            self.financial_table.setItem(row_position, 4, QtWidgets.QTableWidgetItem(filename))

        self.total_invoiced_label.setText(f"Total Invoiced: R{total_invoiced:.2f}")
        self.total_paid_label.setText(f"Total Paid: R{total_paid:.2f}")
        self.balance_label.setText(f"Balance: R{balance:.2f}")

    def mark_as_paid(self):
        selected_rows = self.financial_table.selectionModel().selectedRows()
        if not selected_rows:
            QMessageBox.warning(self, "Selection Error", "Please select an invoice to mark as paid.")
            return
        for selected in selected_rows:
            row = selected.row()
            invoice_number = self.financial_table.item(row, 0).text()
            current_status = self.financial_table.item(row, 3).text()
            if current_status.lower() == "paid":
                QMessageBox.information(self, "Already Paid", f"Invoice {invoice_number} is already marked as paid.")
                continue
            # Update status in metadata
            invoice_number_key = str(invoice_number)
            if invoice_number_key in self.parent.invoices_metadata:
                self.parent.invoices_metadata[invoice_number_key]["status"] = "Paid"
                save_json(self.parent.invoices_metadata, INVOICES_METADATA_FILE)
                self.financial_table.setItem(row, 3, QtWidgets.QTableWidgetItem("Paid"))
                self.parent.log_message(f"Invoice {invoice_number} marked as paid.")
            else:
                QMessageBox.warning(self, "Metadata Error", f"No metadata found for invoice {invoice_number}.")

        # Refresh financial statements
        self.load_financial_statements()

# Email Configuration Tab
class EmailConfigTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Email configuration form
        form_group = QtWidgets.QGroupBox("Email Configuration")
        form_layout = QVBoxLayout()
        self.sender_email_input = QLineEdit(self)
        self.sender_email_input.setPlaceholderText("Sender Email")
        self.sender_password_input = QLineEdit(self)
        self.sender_password_input.setPlaceholderText("Sender Password")
        self.sender_password_input.setEchoMode(QLineEdit.EchoMode.Password)
        self.smtp_server_input = QLineEdit(self)
        self.smtp_server_input.setPlaceholderText("SMTP Server (e.g., smtp.office365.com)")
        self.smtp_port_input = QLineEdit(self)
        self.smtp_port_input.setPlaceholderText("SMTP Port (e.g., 587)")

        form_layout.addWidget(QLabel("Sender Email:"))
        form_layout.addWidget(self.sender_email_input)
        form_layout.addWidget(QLabel("Sender Password:"))
        form_layout.addWidget(self.sender_password_input)
        form_layout.addWidget(QLabel("SMTP Server:"))
        form_layout.addWidget(self.smtp_server_input)
        form_layout.addWidget(QLabel("SMTP Port:"))
        form_layout.addWidget(self.smtp_port_input)

        form_group.setLayout(form_layout)
        layout.addWidget(form_group)

        # Save button
        self.save_email_config_button = QPushButton("Save Email Settings")
        self.save_email_config_button.clicked.connect(self.save_email_config)
        layout.addWidget(self.save_email_config_button)

        self.setLayout(layout)
        self.load_existing_config()

    def load_existing_config(self):
        config = self.parent.email_config
        if config:
            self.sender_email_input.setText(config.get("sender_email", ""))
            self.sender_password_input.setText(config.get("sender_password", ""))
            self.smtp_server_input.setText(config.get("smtp_server", ""))
            self.smtp_port_input.setText(str(config.get("smtp_port", "")))

    def save_email_config(self):
        sender_email = self.sender_email_input.text().strip()
        sender_password = self.sender_password_input.text().strip()
        smtp_server = self.smtp_server_input.text().strip()
        smtp_port = self.smtp_port_input.text().strip()

        if not sender_email or not sender_password or not smtp_server or not smtp_port:
            QMessageBox.warning(self, "Input Error", "Please fill in all fields.")
            return

        try:
            smtp_port = int(smtp_port)
        except ValueError:
            QMessageBox.warning(self, "Input Error", "SMTP Port must be a number.")
            return

        self.parent.email_config = {
            "sender_email": sender_email,
            "sender_password": sender_password,
            "smtp_server": smtp_server,
            "smtp_port": smtp_port
        }
        save_json(self.parent.email_config, EMAIL_CONFIG_FILE)
        self.parent.log_message("Email settings updated.")
        QMessageBox.information(self, "Success", "Email settings have been saved.")

# Company Details Tab
class CompanyDetailsTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Company details form
        form_group = QtWidgets.QGroupBox("Company Details")
        form_layout = QVBoxLayout()
        self.company_name_input = QLineEdit(self)
        self.company_name_input.setPlaceholderText("Company Name")
        self.company_address_input = QLineEdit(self)
        self.company_address_input.setPlaceholderText("Company Address")
        self.company_email_input = QLineEdit(self)
        self.company_email_input.setPlaceholderText("Company Email")
        self.company_phone_input = QLineEdit(self)
        self.company_phone_input.setPlaceholderText("Company Phone")
        self.company_logo_path_input = QLineEdit(self)
        self.company_logo_path_input.setPlaceholderText("Logo Path")
        self.browse_logo_button = QPushButton("Browse")
        self.browse_logo_button.clicked.connect(self.browse_logo)

        logo_layout = QHBoxLayout()
        logo_layout.addWidget(self.company_logo_path_input)
        logo_layout.addWidget(self.browse_logo_button)

        form_layout.addWidget(QLabel("Company Name:"))
        form_layout.addWidget(self.company_name_input)
        form_layout.addWidget(QLabel("Company Address:"))
        form_layout.addWidget(self.company_address_input)
        form_layout.addWidget(QLabel("Company Email:"))
        form_layout.addWidget(self.company_email_input)
        form_layout.addWidget(QLabel("Company Phone:"))
        form_layout.addWidget(self.company_phone_input)
        form_layout.addWidget(QLabel("Company Logo:"))
        form_layout.addLayout(logo_layout)

        form_group.setLayout(form_layout)
        layout.addWidget(form_group)

        # Save button
        self.save_company_details_button = QPushButton("Save Company Details")
        self.save_company_details_button.clicked.connect(self.save_company_details)
        layout.addWidget(self.save_company_details_button)

        self.setLayout(layout)
        self.load_existing_details()

    def browse_logo(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "Select Logo", "", "Image Files (*.png *.jpg *.bmp)")
        if file_path:
            self.company_logo_path_input.setText(file_path)

    def load_existing_details(self):
        details = self.parent.company_details
        if details:
            self.company_name_input.setText(details.get("company_name", ""))
            self.company_address_input.setText(details.get("company_address", ""))
            self.company_email_input.setText(details.get("company_email", ""))
            self.company_phone_input.setText(details.get("company_phone", ""))
            self.company_logo_path_input.setText(details.get("company_logo_path", ""))

    def save_company_details(self):
        company_name = self.company_name_input.text().strip()
        company_address = self.company_address_input.text().strip()
        company_email = self.company_email_input.text().strip()
        company_phone = self.company_phone_input.text().strip()
        company_logo_path = self.company_logo_path_input.text().strip()

        if not company_name or not company_address or not company_email or not company_phone:
            QMessageBox.warning(self, "Input Error", "Please fill in all required fields.")
            return

        self.parent.company_details = {
            "company_name": company_name,
            "company_address": company_address,
            "company_email": company_email,
            "company_phone": company_phone,
            "company_logo_path": company_logo_path
        }
        save_json(self.parent.company_details, COMPANY_DETAILS_FILE)
        self.parent.log_message("Company details updated.")
        QMessageBox.information(self, "Success", "Company details have been saved.")

# Template Management Tab
class TemplateManagementTab(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # List of templates
        self.templates_list = QListWidget()
        self.load_templates()
        layout.addWidget(QLabel("Existing Templates:"))
        layout.addWidget(self.templates_list)

        # Buttons to create, edit, and delete templates
        btn_layout = QHBoxLayout()
        self.create_template_button = QPushButton("Create New Template")
        self.create_template_button.clicked.connect(self.create_template)
        self.edit_template_button = QPushButton("Edit Selected")
        self.edit_template_button.clicked.connect(self.edit_template)
        self.delete_template_button = QPushButton("Delete Selected")
        self.delete_template_button.clicked.connect(self.delete_template)
        btn_layout.addWidget(self.create_template_button)
        btn_layout.addWidget(self.edit_template_button)
        btn_layout.addWidget(self.delete_template_button)
        layout.addLayout(btn_layout)

        self.setLayout(layout)

    def load_templates(self):
        self.templates_list.clear()
        for template in self.parent.templates.keys():
            item = QListWidgetItem(template)
            self.templates_list.addItem(item)

    def create_template(self):
        """Open CreateTemplate.py to create a new template."""
        try:
            # Assuming CreateTemplate.py is in the same directory and is executable
            result = os.system("python CreateTemplate.py")
            if result == 0:
                self.parent.log_message("Opened CreateTemplate.py")
                # Reload templates after creation
                self.load_templates()
            else:
                QMessageBox.warning(self, "Error", "Failed to open CreateTemplate.py.")
        except Exception as e:
            QMessageBox.warning(self, "Error", f"Failed to open CreateTemplate.py: {e}")

    def edit_template(self):
        """Open EditTemplate.py to edit the selected template."""
        selected_item = self.templates_list.currentItem()
        if not selected_item:
            QMessageBox.warning(self, "Selection Error", "Please select a template to edit.")
            return
        template_name = selected_item.text()
        try:
            # Assuming EditTemplate.py can accept template name as argument
            result = os.system(f"python EditTemplate.py {template_name}")
            if result == 0:
                self.parent.log_message(f"Opened EditTemplate.py for template '{template_name}'")
                # Reload templates after editing
                self.load_templates()
            else:
                QMessageBox.warning(self, "Error", "Failed to open EditTemplate.py.")
        except Exception as e:
            QMessageBox.warning(self, "Error", f"Failed to open EditTemplate.py: {e}")

    def delete_template(self):
        """Logic to delete the selected template."""
        selected_item = self.templates_list.currentItem()
        if not selected_item:
            QMessageBox.warning(self, "Selection Error", "Please select a template to delete.")
            return
        template_name = selected_item.text()
        reply = QMessageBox.question(
            self, 'Delete Confirmation',
            f"Are you sure you want to delete template '{template_name}'?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No
        )
        if reply == QMessageBox.StandardButton.Yes:
            try:
                del self.parent.templates[template_name]
                save_json(self.parent.templates, TEMPLATES_FILE)
                self.parent.log_message(f"Template '{template_name}' deleted.")
                self.load_templates()
            except Exception as e:
                QMessageBox.warning(self, "Delete Error", f"Failed to delete template: {e}")
                self.parent.log_message(f"Failed to delete template '{template_name}': {e}")

# Run the application
if __name__ == "__main__":
    app = QApplication(sys.argv)

    # Set application icon (optional, ensure you have an icon file)
    # app.setWindowIcon(QtGui.QIcon('icon.png'))

    window = QuickBooksReplacement()
    window.show()
    sys.exit(app.exec())