from __future__ import annotations

from typing import Callable, Optional
from pathlib import Path

from PySide6.QtCore import Qt, QTimer, QDate
from PySide6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTextEdit,
    QPushButton, QMessageBox, QTabWidget, QTableWidget, QTableWidgetItem,
    QHeaderView, QAbstractItemView, QSizePolicy, QDateEdit, QDoubleSpinBox
)
from PySide6.QtGui import QFontDatabase, QFont

from mi100.repository import Repo
from mi100.ui.delegates import RowTintDelegate
from mi100.ui.report_dialog import ReportDialog
from mi100.ui.report_history import ReportHistoryTab
from mi100.services.project_folders import open_folder


class VoiceActorDetail(QWidget):
    def __init__(
        self,
        repo: Repo,
        voice_actor_id: Optional[int] = None,
        on_saved: Optional[Callable[[int], None]] = None,
        on_deleted: Optional[Callable[[int], None]] = None,
    ):
        super().__init__()
        self.repo = repo
        self.voice_actor_id = voice_actor_id
        self.on_saved = on_saved
        self.on_deleted = on_deleted

        self._dirty = False
        self._has_user_input = False
        self._save_timer = QTimer(self)
        self._save_timer.setSingleShot(True)
        self._save_timer.timeout.connect(self._save)

        self._build_ui()
        self._load()

    def _build_ui(self):
        root = QVBoxLayout(self)
        root.setContentsMargins(12, 12, 12, 12)
        root.setSpacing(10)

        self._title_label = QLabel("")
        self._title_label.hide()

        self.tabs = QTabWidget()
        root.addWidget(self.tabs, 1)

        # -------------------- TAB 1: PROJECTS --------------------
        tab_projects = QWidget()
        l1 = QVBoxLayout(tab_projects)
        l1.setContentsMargins(0, 0, 0, 0)
        l1.setSpacing(8)

        top = QHBoxLayout()
        self.projects_balance_label = QLabel('Баланс\n₴ 0.00'); self.projects_balance_label.setFont(_best_currency_font(11, True))
        self.projects_balance_label.setStyleSheet('font-weight:700; font-size:11pt; color:#111827; background:transparent; border:none;')
        self.projects_debt_label = QLabel('Борг\n₴ 0.00'); self.projects_debt_label.setFont(_best_currency_font(11, True))
        self.projects_debt_label.setStyleSheet('font-weight:700; font-size:11pt; color:#111827; background:transparent; border:none;')
        top.addWidget(self.projects_balance_label, 0, Qt.AlignLeft)
        top.addSpacing(16)
        top.addWidget(self.projects_debt_label, 0, Qt.AlignLeft)
        top.addStretch(1)
        self.btn_pay_all = QPushButton("Оплатити всі")
        self.btn_pay_all.setMinimumHeight(24)
        self.btn_pay_all.clicked.connect(self._pay_all)
        self._apply_pay_all_style(False)
        top.addWidget(self.btn_pay_all, 0, Qt.AlignLeft)

        l1.addLayout(top)

        self.projects_table = QTableWidget(0, 5)
        self.projects_table.setHorizontalHeaderLabels(["Дата", "Проєкт", "Гонорар", "Папка проєкту", "Оплата"])
        self.projects_table.verticalHeader().setVisible(False)
        self.projects_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.projects_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.projects_table.setSelectionMode(QAbstractItemView.SingleSelection)

        self.projects_table.setWordWrap(True)
        self.projects_table.horizontalHeader().setMinimumSectionSize(56)
        self.projects_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.projects_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.projects_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.projects_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.projects_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        self.projects_table.setColumnWidth(0, 96)
        self.projects_table.setColumnWidth(1, 320)
        self.projects_table.setColumnWidth(2, 110)
        self.projects_table.setColumnWidth(3, 190)
        self.projects_table.setColumnWidth(4, 130)

        self.projects_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.projects_table.setMouseTracking(True)

        self._row_delegate = RowTintDelegate(self.projects_table)
        self.projects_table.setItemDelegate(self._row_delegate)
        self.projects_table.cellEntered.connect(
            lambda r, c: (self._row_delegate.set_hover_row(r), self.projects_table.viewport().update())
        )

        l1.addWidget(self.projects_table, 1)

        # -------------------- TAB 2: ACTOR INFO --------------------
        tab_info = QWidget()
        l2 = QVBoxLayout(tab_info)
        l2.setContentsMargins(0, 0, 0, 0)
        l2.setSpacing(8)
        l2.addSpacing(10)

        self.ed_name = QLineEdit()
        self.ed_name.setMinimumWidth(260)
        self.ed_name.setPlaceholderText("Ім'я / псевдонім")
        self.ed_name.setMinimumHeight(32)

        self.ed_phone = QLineEdit()
        self.ed_phone.setMinimumWidth(260)
        self.ed_phone.setPlaceholderText("Телефон")
        self.ed_phone.setMinimumHeight(32)

        self.ed_email = QLineEdit()
        self.ed_email.setMinimumWidth(260)
        self.ed_email.setPlaceholderText("Email")
        self.ed_email.setMinimumHeight(32)

        self.ed_payment = QTextEdit()
        self.ed_payment.setMinimumWidth(260)
        self.ed_payment.setPlaceholderText("Реквізити / деталі оплати")
        self.ed_payment.setFixedHeight(110)

        self.ed_notes = QTextEdit()
        self.ed_notes.setMinimumWidth(260)
        self.ed_notes.setPlaceholderText("Нотатки")
        self.ed_notes.setFixedHeight(130)

        self._field_label_width = 120
        for label, widget in [
            ("Ім'я", self.ed_name),
            ("Телефон", self.ed_phone),
            ("Email", self.ed_email),
            ("Реквізити", self.ed_payment),
            ("Нотатки", self.ed_notes),
        ]:
            row = QHBoxLayout()
            row.setContentsMargins(0, 0, 0, 0)
            row.setSpacing(10)
            lbl = QLabel(label)
            lbl.setFixedWidth(self._field_label_width)
            lbl.setAlignment(Qt.AlignRight | Qt.AlignTop)
            lbl.setStyleSheet("font-size:10pt; font-weight:700; color:#1F2937; background:transparent; border:none; padding:0; margin:0;")
            row.addWidget(lbl, 0, Qt.AlignTop)
            row.addWidget(widget, 0, Qt.AlignLeft | Qt.AlignTop)
            row.addStretch(1)
            l2.addLayout(row)

        l2.addStretch(1)

        bottom = QHBoxLayout()
        bottom.setContentsMargins(0, 8, 0, 0)

        self.btn_delete = QPushButton("Видалити підрядника")
        self.btn_delete.setToolTip("Видалити підрядника")
        self.btn_delete.setObjectName("Danger")
        self.btn_delete.setMinimumHeight(24)
        self.btn_delete.setStyleSheet("QPushButton{background:#FFA8A8;color:#7C2D12;border:1px solid #E59494;border-radius:4px;padding:4px 10px;font-weight:600;} QPushButton:hover{background:#E59494;}")
        self.btn_delete.clicked.connect(self._delete)
        bottom.addWidget(self.btn_delete, 0, Qt.AlignLeft)
        bottom.addStretch(1)

        l2.addLayout(bottom)

        tab_payments = QWidget()
        lp = QVBoxLayout(tab_payments)
        lp.setContentsMargins(0, 0, 0, 0)
        lp.setSpacing(8)
        pay_top = QHBoxLayout()
        self.payments_balance_label = QLabel('Баланс\n₴ 0.00'); self.payments_balance_label.setFont(_best_currency_font(11, True))
        self.payments_balance_label.setStyleSheet('font-weight:700; font-size:11pt; color:#111827; background:transparent; border:none;')
        pay_top.addWidget(self.payments_balance_label)
        pay_top.addStretch(1)
        lp.addLayout(pay_top)
        add_pay = QHBoxLayout()
        self.payment_date = QDateEdit(); self.payment_date.setCalendarPopup(True); self.payment_date.setDisplayFormat('yyyy-MM-dd'); self.payment_date.setDate(QDate.currentDate()); self.payment_date.setMinimumWidth(120)
        self.payment_amount = QDoubleSpinBox(); self.payment_amount.setFont(_best_currency_font()); self.payment_amount.setDecimals(2); self.payment_amount.setMaximum(1_000_000_000); self.payment_amount.setMinimum(0); self.payment_amount.setButtonSymbols(QDoubleSpinBox.NoButtons); self.payment_amount.setMinimumWidth(130); self.payment_amount.setPrefix('₴ ')
        from PySide6.QtWidgets import QComboBox
        self.payment_currency = QComboBox(); self.payment_currency.setMinimumWidth(90); self.payment_currency.currentIndexChanged.connect(self._sync_payment_currency_prefix)
        self.payment_comment = QLineEdit(); self.payment_comment.setPlaceholderText('Коментар до оплати (необов’язково)')
        self.btn_add_payment = QPushButton('Внести оплату'); self.btn_add_payment.setObjectName('Primary'); self.btn_add_payment.setMinimumHeight(22); self.btn_add_payment.setMinimumWidth(150); self.btn_add_payment.setStyleSheet("QPushButton{background:#4FAF7A; color:#FFFFFF; border:1px solid #449C6E; border-radius:6px; padding:2px 12px; font-weight:600;} QPushButton:hover{background:#449C6E;} QPushButton:disabled{background:#A7D6BA; color:white; border:1px solid #8EC6A5;}"); self.btn_add_payment.clicked.connect(self._add_payment)
        lbl_pay_date = QLabel('Дата:'); lbl_pay_date.setStyleSheet("font-weight:700; color:#111827; background:transparent; border:none;"); lbl_pay_sum = QLabel('Сума:'); lbl_pay_sum.setStyleSheet("font-weight:700; color:#111827; background:transparent; border:none;"); add_pay.addWidget(lbl_pay_date); add_pay.addWidget(self.payment_date); add_pay.addWidget(lbl_pay_sum); add_pay.addWidget(self.payment_amount); add_pay.addWidget(self.payment_currency); add_pay.addWidget(self.payment_comment, 1); add_pay.addWidget(self.btn_add_payment)
        lp.addLayout(add_pay)
        self.payments_table = QTableWidget(0, 5)
        self.payments_table.setHorizontalHeaderLabels(['Дата', 'Сума', 'Валюта', 'Коментар', 'Видалити'])
        self.payments_table.verticalHeader().setVisible(False)
        self.payments_table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.payments_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.payments_table.setSelectionMode(QAbstractItemView.SingleSelection)
        self.payments_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.payments_table.setWordWrap(True)
        self.payments_table.setMouseTracking(True)
        self.payments_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.payments_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.payments_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.payments_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.payments_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
        self.payments_table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeToContents)
        self._payments_delegate = RowTintDelegate(self.payments_table)
        self.payments_table.setItemDelegate(self._payments_delegate)
        self.payments_table.cellEntered.connect(lambda row, _col: (self._payments_delegate.set_hover_row(row), self.payments_table.viewport().update()))
        lp.addWidget(self.payments_table)

        self.tabs.addTab(tab_projects, "Проєкти")
        self.tabs.addTab(tab_payments, "Оплати")
        self.reports_tab = ReportHistoryTab(self.repo, 'actor', lambda: self.voice_actor_id, self, generate_callback=self._open_report_dialog, show_templates_button=False)
        self.tabs.addTab(self.reports_tab, "Звіти")
        self.tabs.addTab(tab_info, "Дані підрядника")
        self.tabs.setCurrentIndex(0)

        for w, sig in [
            (self.ed_name, "textChanged"),
            (self.ed_phone, "textChanged"),
            (self.ed_email, "textChanged"),
            (self.ed_payment, "textChanged"),
            (self.ed_notes, "textChanged"),
        ]:
            getattr(w, sig).connect(self._mark_dirty)

    def _apply_pay_all_style(self, inactive: bool) -> None:
        if inactive:
            self.btn_pay_all.setStyleSheet(
                "QPushButton{background:#EBEBEB;color:#6B7280;border:1px solid #D7D7D7;"
                "border-radius:4px;padding:4px 10px;font-weight:600;}"
            )
        else:
            self.btn_pay_all.setStyleSheet(
                "QPushButton{background:#FFD8A8;color:#8E6129;border:1px solid #E5BF90;"
                "border-radius:4px;padding:4px 10px;font-weight:600;}"
                "QPushButton:hover{background:#E5BF90;}"
            )



    def _make_table_button_holder(self, key: str, button: QPushButton) -> QWidget:
        holder = QWidget()
        holder.setAttribute(Qt.WA_StyledBackground, True)
        holder.setAutoFillBackground(False)
        holder.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        holder.setMinimumSize(0, 0)
        holder.setStyleSheet("background:transparent; border:none; margin:0; padding:0;")
        hlay = QHBoxLayout(holder)
        hlay.setContentsMargins(0, 0, 0, 0)
        hlay.setSpacing(0)
        hlay.setAlignment(Qt.AlignCenter)
        hlay.addWidget(button, 0, Qt.AlignCenter)
        return holder

    def _row_bg_css(self, key: str) -> str:
        key = (key or "white").lower().strip()
        mapping = {
            "green": "rgb(210, 242, 220)",
            "yellow": "rgb(255, 245, 205)",
            "red": "rgb(252, 232, 232)",
            "gray": "rgb(235, 235, 235)",
        }
        return mapping.get(key, "rgb(255, 255, 255)")

    def _currency_symbol(self, code: str) -> str:
        code = str(code or 'UAH').upper()
        return {'UAH': '₴', 'USD': '$', 'EUR': '€'}.get(code, code)

    def _money(self, value: float, currency: str = 'UAH') -> str:
        return f"{self._currency_symbol(currency)} {float(value or 0):.2f}"

    def _format_currency_map(self, values: dict[str, float]) -> str:
        ordered = []
        for code in ('UAH','USD','EUR'):
            if code in values:
                ordered.append(f"{self._currency_symbol(code)} {float(values.get(code, 0.0)):.2f}")
        if not ordered:
            ordered = ['₴ 0.00']
        return '\n'.join(ordered)

    def _sync_payment_currency_prefix(self) -> None:
        cur = self.payment_currency.currentData() or self.payment_currency.currentText() or 'UAH'
        self.payment_amount.setPrefix(f"{self._currency_symbol(cur)} ")

    def _update_balance_labels(self) -> None:
        balance = self.repo.get_voice_actor_balance_map(int(self.voice_actor_id)) if self.voice_actor_id else {'UAH': 0.0}
        debt = self.repo.get_voice_actor_debt_map(int(self.voice_actor_id)) if self.voice_actor_id else {'UAH': 0.0}
        self.projects_balance_label.setText('Баланс\n' + self._format_currency_map(balance))
        self.payments_balance_label.setText('Баланс\n' + self._format_currency_map(balance))
        self.projects_debt_label.setText('Борг\n' + self._format_currency_map(debt))

    def _load(self):
        if not self.voice_actor_id:
            self._title_label.setText("")
            self.btn_delete.setEnabled(False)
            self._fill_projects([])
            self._refresh_payments()
            self.reports_tab.refresh()
            self._has_user_input = False
            self._set_dirty(False)
            self.tabs.setCurrentIndex(3)
            return

        va = self.repo.get_voice_actor(int(self.voice_actor_id))
        if not va:
            self.btn_delete.setEnabled(False)
            self._fill_projects([])
            self._refresh_payments()
            self.reports_tab.refresh()
            self._set_dirty(False)
            return

        self._block_all(True)
        self.ed_name.setText(va["display_name"] or "")
        self._title_label.setText(va["display_name"] or "")
        self.ed_phone.setText(va["phone"] or "")
        self.ed_email.setText(va["email"] or "")
        self.ed_payment.setPlainText(va["payment_details_text"] or "")
        self.ed_notes.setPlainText(va["notes"] or "")
        self._block_all(False)

        self.btn_delete.setEnabled(True)
        self._fill_projects(self.repo.list_voice_actor_project_items(int(self.voice_actor_id)))
        self._refresh_payments()
        self.reports_tab.refresh()
        self._set_dirty(False)

    def _fill_projects(self, rows):
        self.projects_table.setRowCount(len(rows))
        unpaid_count = 0

        for row_idx, r in enumerate(rows):
            created = str(r.get("created_date") or "")
            title = str(r.get("title") or "")
            fee = float(r.get("fee") or 0.0)
            paid_flag = int(r.get("paid_to_actor") or 0)
            folder_path = str(r.get('project_folder_path') or '')
            key = "gray" if paid_flag else ("green" if fee > 0 else "white")

            if paid_flag == 0 and fee > 0:
                unpaid_count += 1

            for col, value in enumerate((created, title, f"{fee:.2f}")):
                item = QTableWidgetItem(value)
                item.setData(RowTintDelegate.ROLE_BG_KEY, key)
                if col == 2:
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                self.projects_table.setItem(row_idx, col, item)

            if folder_path and Path(folder_path).exists():
                btn_folder = QPushButton('Відкрити папку проєкту')
                btn_folder.setFixedHeight(22)
                btn_folder.setMinimumWidth(200)
                btn_folder.setStyleSheet(
                    "QPushButton{background:#FFD8A8;color:#8E6129;border:1px solid #E5BF90;"
                    "border-radius:4px;padding:4px 10px;font-weight:600;}"
                    "QPushButton:hover{background:#E5BF90;}"
                )
                btn_folder.clicked.connect(lambda _=False, p=folder_path: open_folder(p))
                self.projects_table.setCellWidget(row_idx, 3, self._make_table_button_holder(key, btn_folder))
            else:
                no_folder = QLabel('Папка не створена'); no_folder.setAlignment(Qt.AlignCenter); no_folder.setStyleSheet('color:#6B7280; background:transparent; border:none;'); self.projects_table.setCellWidget(row_idx, 3, self._make_table_button_holder(key, no_folder))

            if fee <= 0:
                lbl = QLabel('Безкоштовно')
                lbl.setAlignment(Qt.AlignCenter)
                lbl.setStyleSheet('font-weight:600; color:#6B7280;')
                self.projects_table.setCellWidget(row_idx, 4, self._make_table_button_holder(key, lbl))
            else:
                can_pay = int(r.get('payment_possible') or 0) == 1
                btn = QPushButton("Скасувати оплату" if paid_flag else "Оплатити")
                btn.setFixedHeight(22)
                btn.setMinimumWidth(136)

                if paid_flag:
                    btn.setStyleSheet(
                        "QPushButton{background:#4FAF7A;color:#FFFFFF;border:1px solid #459E6E;"
                        "border-radius:4px;padding:4px 10px;font-weight:600;}"
                        "QPushButton:hover{background:#459E6E;}"
                    )
                elif can_pay:
                    btn.setStyleSheet(
                        "QPushButton{background:#FFD8A8;color:#8E6129;border:1px solid #E5BF90;"
                        "border-radius:4px;padding:4px 10px;font-weight:600;}"
                        "QPushButton:hover{background:#E5BF90;}"
                    )
                else:
                    btn.setStyleSheet("QPushButton{background:#EBEBEB;color:#6B7280;border:1px solid #D7D7D7;border-radius:4px;padding:4px 10px;font-weight:600;}")

                project_id = int(r.get("project_id"))
                btn.clicked.connect(
                    lambda _=False, pid=project_id, cur_paid=paid_flag: self._toggle_project_paid(pid, cur_paid)
                )
                holder = self._make_table_button_holder(key, btn)
                self.projects_table.setCellWidget(row_idx, 4, holder)
        self.projects_table.resizeColumnsToContents()
        self.projects_table.setColumnWidth(1, max(260, self.projects_table.columnWidth(1)))
        self.projects_table.setColumnWidth(3, max(190, self.projects_table.columnWidth(3)))
        self.projects_table.setColumnWidth(4, max(130, self.projects_table.columnWidth(4)))
        self.projects_table.resizeRowsToContents()
        for row_idx in range(self.projects_table.rowCount()):
            self.projects_table.setRowHeight(row_idx, max(18, min(22, self.projects_table.rowHeight(row_idx))))

        self.btn_pay_all.setEnabled(unpaid_count > 0)
        self._apply_pay_all_style(unpaid_count == 0)


    def _refresh_payments(self) -> None:
        rows = self.repo.list_voice_actor_payments(self.voice_actor_id) if self.voice_actor_id else []
        self.payments_table.setRowCount(0)
        self._update_balance_labels()
        for row in rows:
            r = self.payments_table.rowCount(); self.payments_table.insertRow(r)
            vals = [str(row.get('payment_date') or ''), self._money(float(row.get('amount') or 0.0), str(row.get('currency') or 'UAH')), self._currency_symbol(str(row.get('currency') or 'UAH')), str(row.get('comment') or '')]
            for c, v in enumerate(vals):
                it = QTableWidgetItem(v)
                if c == 1:
                    it.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                it.setData(RowTintDelegate.ROLE_BG_KEY, 'blue')
                self.payments_table.setItem(r, c, it)
            btn_del = QPushButton('Видалити')
            btn_del.setFixedHeight(22)
            btn_del.setMinimumWidth(96)
            btn_del.setStyleSheet("QPushButton{color:#7C2D12; background:#FFA8A8; border:1px solid #E59494; border-radius:6px; padding:2px 8px; font-weight:600;} QPushButton:hover{background:#E59494;}")
            btn_del.clicked.connect(lambda _=False, pay_id=int(row['id']): self._delete_payment(pay_id))
            self.payments_table.setCellWidget(r, 4, self._make_table_button_holder('blue', btn_del))
        self.payments_table.resizeColumnsToContents()
        self.payments_table.setColumnWidth(3, max(260, self.payments_table.columnWidth(3)))
        self.payments_table.resizeRowsToContents()
        for row_idx in range(self.payments_table.rowCount()):
            self.payments_table.setRowHeight(row_idx, max(18, min(22, self.payments_table.rowHeight(row_idx))))

    def _after_financial_change(self):
        self._fill_projects(self.repo.list_voice_actor_project_items(int(self.voice_actor_id)) if self.voice_actor_id else [])
        self._refresh_payments()
        self.reports_tab.refresh()
        if self.on_saved and self.voice_actor_id:
            self.on_saved(int(self.voice_actor_id))

    def _pay_all_possible(self) -> int:
        rows = self.repo.list_voice_actor_project_items(self.voice_actor_id) if self.voice_actor_id else []
        changed = 0
        for row in rows:
            if float(row.get('fee') or 0.0) <= 0 or int(row.get('paid_to_actor') or 0) != 0:
                continue
            try:
                self.repo.set_voice_actor_project_paid(int(self.voice_actor_id), int(row['project_id']), True)
                changed += 1
            except ValueError:
                continue
        return changed

    def _open_report_dialog(self) -> None:
        if not self.voice_actor_id:
            return
        dlg = ReportDialog(self.repo, 'actor', int(self.voice_actor_id), self)
        if dlg.exec():
            self.reports_tab.refresh()

    def _toggle_project_paid(self, project_id: int, paid: bool):
        if not self.voice_actor_id:
            return
        try:
            self.repo.set_voice_actor_project_paid(
                int(self.voice_actor_id),
                int(project_id),
                not bool(paid),
            )
        except ValueError:
            QMessageBox.warning(self, 'Недостатній баланс', 'Поточний баланс підрядника не дозволяє провести оплату цього проєкту')
            return
        self._after_financial_change()

    def _pay_all(self):
        if not self.voice_actor_id:
            return
        rows = self.repo.list_voice_actor_project_items(self.voice_actor_id)
        unpaid = [r for r in rows if float(r.get('fee') or 0.0) > 0 and int(r.get('paid_to_actor') or 0) == 0]
        if not unpaid:
            return
        insufficient = False
        for row in unpaid:
            try:
                self.repo.set_voice_actor_project_paid(int(self.voice_actor_id), int(row['project_id']), True)
            except ValueError:
                insufficient = True
                break
        if insufficient:
            box = QMessageBox(self)
            box.setIcon(QMessageBox.Warning)
            box.setWindowTitle('Недостатній баланс')
            box.setText('Поточний баланс підрядника не дозволяє провести оплату всіх проєктів')
            pay_possible_btn = box.addButton('Оплатити всі можливі', QMessageBox.AcceptRole)
            box.addButton('Закрити', QMessageBox.RejectRole)
            box.exec()
            if box.clickedButton() is pay_possible_btn:
                self._pay_all_possible()
        self._after_financial_change()

    def _add_payment(self) -> None:
        if not self.voice_actor_id:
            QMessageBox.warning(self, 'Увага', 'Спочатку збережіть підрядника.')
            return
        amount = float(self.payment_amount.value())
        if amount <= 0:
            QMessageBox.warning(self, 'Увага', 'Вкажіть суму оплати більше нуля.')
            return
        self.repo.add_voice_actor_payment(int(self.voice_actor_id), self.payment_date.date().toString('yyyy-MM-dd'), amount, self.payment_currency.currentData() or self.payment_currency.currentText() or 'UAH', self.payment_comment.text())
        self.payment_amount.setValue(0.0)
        self.payment_currency.setCurrentIndex(0)
        self.payment_comment.clear()
        self._after_financial_change()

    def _delete_payment(self, payment_id: int) -> None:
        box = QMessageBox.question(self, 'Видалення', 'Видалити цю оплату?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if box != QMessageBox.Yes:
            return
        self.repo.delete_voice_actor_payment(int(payment_id))
        self._after_financial_change()

    def _block_all(self, block: bool):
        for w in [self.ed_name, self.ed_phone, self.ed_email, self.ed_payment, self.ed_notes]:
            w.blockSignals(block)

    def _set_dirty(self, dirty: bool):
        self._dirty = bool(dirty)

    def _mark_dirty(self, *args):
        self._has_user_input = self._snapshot_has_user_input()
        self._set_dirty(True)
        self._save_timer.start(600)

    def _save(self):
        if not self._dirty:
            return

        name = (self.ed_name.text() or "").strip()
        if not name:
            self._has_user_input = self._snapshot_has_user_input()
            return

        payload = dict(
            display_name=name,
            phone=(self.ed_phone.text() or "").strip(),
            email=(self.ed_email.text() or "").strip(),
            payment_details=(self.ed_payment.toPlainText() or "").strip(),
            notes=(self.ed_notes.toPlainText() or "").strip(),
            is_active=True,
        )

        if not self.voice_actor_id:
            self.voice_actor_id = self.repo.create_voice_actor(**payload)
            self.btn_delete.setEnabled(True)
        else:
            self.repo.update_voice_actor(int(self.voice_actor_id), **payload)

        self._has_user_input = self._snapshot_has_user_input()
        self._title_label.setText(name)
        self._set_dirty(False)
        self._fill_projects(self.repo.list_voice_actor_project_items(int(self.voice_actor_id)))
        self._refresh_payments()

        if self.on_saved:
            self.on_saved(int(self.voice_actor_id))


    def _snapshot_has_user_input(self) -> bool:
        return any([
            (self.ed_name.text() or "").strip(),
            (self.ed_phone.text() or "").strip(),
            (self.ed_email.text() or "").strip(),
            (self.ed_payment.toPlainText() or "").strip(),
            (self.ed_notes.toPlainText() or "").strip(),
        ])

    def can_close_panel(self) -> bool:
        if self.voice_actor_id:
            return True
        if self._snapshot_has_user_input() and not (self.ed_name.text() or "").strip():
            QMessageBox.warning(self, "Помилка", "Ім'я підрядника є обов'язковим для заповнення")
            self.tabs.setCurrentIndex(3)
            self.ed_name.setFocus()
            return False
        return True

    def _delete(self):
        if not self.voice_actor_id:
            return

        if QMessageBox.question(
            self,
            "Видалити",
            "Видалити підрядника?",
            QMessageBox.Yes | QMessageBox.No
        ) != QMessageBox.Yes:
            return

        self.repo.delete_voice_actor(int(self.voice_actor_id))

        if self.on_deleted:
            self.on_deleted(int(self.voice_actor_id))