from __future__ import annotations

from PySide6.QtCore import Qt, QDate, QUrl
from PySide6.QtGui import QDesktopServices
from PySide6.QtWidgets import (
    QCheckBox,
    QDateEdit,
    QDialog,
    QHBoxLayout,
    QLabel,
    QMessageBox,
    QPushButton,
    QVBoxLayout,
)

from mi100.repository import Repo
from mi100.services.pdf_documents import build_actor_report_pdf, build_client_report_pdf
from mi100.timeutil import today_local_date

BTN_GREEN = (
    "QPushButton{background:#4FAF7A;color:#FFFFFF;border:1px solid #459E6E;"
    "border-radius:4px;padding:4px 10px;font-weight:600;}"
    "QPushButton:hover{background:#459E6E;}"
)


class ReportDialog(QDialog):
    def __init__(self, repo: Repo, entity_type: str, entity_id: int, parent=None):
        super().__init__(parent)
        self.repo = repo
        self.entity_type = entity_type
        self.entity_id = int(entity_id)
        self.setWindowTitle("Формування звіту")
        self.resize(430, 180)

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

        title = QLabel("Оберіть режим формування звіту")
        title.setStyleSheet("font-size:12pt; font-weight:700;")
        root.addWidget(title)

        self.unpaid_only = QCheckBox("Усі неоплачені")
        self.unpaid_only.toggled.connect(self._toggle_unpaid_only)
        root.addWidget(self.unpaid_only)

        self.free_only = QCheckBox("Усі безкоштовні")
        self.free_only.toggled.connect(self._toggle_free_only)
        root.addWidget(self.free_only)
        if self.entity_type == 'client':
            self.free_only.hide()

        dates = QHBoxLayout()
        dates.addWidget(QLabel("Від:"))
        self.start_date = QDateEdit()
        self.start_date.setCalendarPopup(True)
        self.start_date.setDisplayFormat("yyyy-MM-dd")
        self.start_date.setDate(QDate.currentDate().addMonths(-1))
        dates.addWidget(self.start_date)
        dates.addWidget(QLabel("До:"))
        self.end_date = QDateEdit()
        self.end_date.setCalendarPopup(True)
        self.end_date.setDisplayFormat("yyyy-MM-dd")
        self.end_date.setDate(QDate.currentDate())
        dates.addWidget(self.end_date)
        root.addLayout(dates)

        hint = QLabel(
            "Можна комбінувати період, усі неоплачені та усі безкоштовні. У звіт потраплять лише проєкти, що відповідають усім обраним умовам."
        )
        hint.setWordWrap(True)
        hint.setStyleSheet("color:#4B5563;")
        root.addWidget(hint)

        btns = QHBoxLayout()
        btns.addStretch(1)
        gen = QPushButton("Згенерувати")
        gen.setStyleSheet(BTN_GREEN)
        gen.clicked.connect(self._generate)
        btns.addWidget(gen)
        cancel = QPushButton("Скасувати")
        cancel.clicked.connect(self.reject)
        btns.addWidget(cancel)
        root.addLayout(btns)

    def _toggle_dates(self, checked: bool) -> None:
        return

    def _toggle_unpaid_only(self, checked: bool) -> None:
        return

    def _toggle_free_only(self, checked: bool) -> None:
        return

    def _generate(self) -> None:
        created_date = today_local_date()
        if self.entity_type == 'client':
            entity = self.repo.get_client(self.entity_id)
            rows = self.repo.list_client_projects(self.entity_id)
            if not entity:
                QMessageBox.warning(self, "Увага", "Замовника не знайдено.")
                return
            start = self.start_date.date().toString("yyyy-MM-dd")
            end = self.end_date.date().toString("yyyy-MM-dd")
            unpaid_only = self.unpaid_only.isChecked()
            filtered = self._filter_client_rows(rows, start, end, unpaid_only)
            if not filtered:
                QMessageBox.information(self, "Звіт", "Немає проєктів для звіту за обраними умовами.")
                return
            balance_map = self.repo.get_client_balance_map(self.entity_id)
            debt_map = self.repo.get_client_debt_map(self.entity_id)
            path = build_client_report_pdf(
                client=dict(entity),
                rows=filtered,
                start_date=start,
                end_date=end,
                unpaid_only=unpaid_only,
                balance_map=balance_map,
                debt_map=debt_map,
            )
            totals = {}
            for r in filtered:
                if int(r.get('client_paid') or 0) != 0:
                    continue
                cur = str(r.get('currency') or 'UAH')
                totals[cur] = totals.get(cur, 0.0) + float(r.get('total_amount') or 0)
            currency = next(iter(totals.keys()), 'UAH') if len(totals) == 1 else ''
            total_amount = next(iter(totals.values()), 0.0) if len(totals) == 1 else sum(totals.values())
            self.repo.create_document(
                doc_type='CLIENT_REPORT',
                client_id=self.entity_id,
                voice_actor_id=None,
                doc_number=self.repo.next_document_number('CLIENT_REPORT'),
                currency=currency or 'MULTI',
                total_amount=total_amount,
                created_date=created_date,
                file_path=path,
                notes=self.repo._report_period_note(start, end, unpaid_only),
                projects=filtered,
            )
        else:
            entity = self.repo.get_voice_actor(self.entity_id)
            rows = self.repo.list_voice_actor_project_items(self.entity_id)
            if not entity:
                QMessageBox.warning(self, "Увага", "Підрядника не знайдено.")
                return
            for row in rows:
                project = self.repo.get_project(int(row.get('project_id') or 0)) if row.get('project_id') else None
                row['currency'] = str(row.get('currency') or 'UAH')
                row['fee_currency'] = str(row.get('fee_currency') or row.get('currency') or 'UAH')
                row['id'] = row.get('project_id')
                row['project_number'] = (project or {}).get('project_number') or 0
                row['total_amount'] = row.get('fee') or 0
            start = self.start_date.date().toString("yyyy-MM-dd")
            end = self.end_date.date().toString("yyyy-MM-dd")
            unpaid_only = self.unpaid_only.isChecked()
            free_only = self.free_only.isChecked()
            filtered = self._filter_actor_rows(rows, start, end, unpaid_only, free_only)
            if not filtered:
                QMessageBox.information(self, "Звіт", "Немає проєктів для звіту за обраними умовами.")
                return
            path = build_actor_report_pdf(
                actor=dict(entity),
                rows=filtered,
                start_date=start,
                end_date=end,
                unpaid_only=unpaid_only,
                free_only=free_only,
            )
            totals = {}
            for r in filtered:
                if int(r.get('paid_to_actor') or 0) != 0:
                    continue
                cur = str(r.get('fee_currency') or r.get('currency') or 'UAH')
                totals[cur] = totals.get(cur, 0.0) + float(r.get('fee') or 0)
            currency = next(iter(totals.keys()), 'UAH') if len(totals) == 1 else ''
            total_amount = next(iter(totals.values()), 0.0) if len(totals) == 1 else sum(totals.values())
            self.repo.create_document(
                doc_type='ACTOR_REPORT',
                client_id=None,
                voice_actor_id=self.entity_id,
                doc_number=self.repo.next_document_number('ACTOR_REPORT'),
                currency=currency or 'MULTI',
                total_amount=total_amount,
                created_date=created_date,
                file_path=path,
                notes=self.repo._report_period_note(start, end, unpaid_only, free_only),
                projects=filtered,
            )
        QDesktopServices.openUrl(QUrl.fromLocalFile(path))
        self.accept()

    @staticmethod
    def _filter_client_rows(rows: list[dict], start: str, end: str, unpaid_only: bool) -> list[dict]:
        out = []
        for r in rows:
            dt = str(r.get('created_date') or '')
            if not (start <= dt <= end):
                continue
            if unpaid_only and int(r.get('client_paid') or 0) != 0:
                continue
            out.append(dict(r))
        return out

    @staticmethod
    def _filter_actor_rows(rows: list[dict], start: str, end: str, unpaid_only: bool, free_only: bool=False) -> list[dict]:
        out = []
        for r in rows:
            dt = str(r.get('created_date') or '')
            if not (start <= dt <= end):
                continue
            fee = float(r.get('fee') or 0.0)
            is_free = abs(fee) < 0.000001
            is_unpaid = int(r.get('paid_to_actor') or 0) == 0 and not is_free
            if unpaid_only or free_only:
                if not ((unpaid_only and is_unpaid) or (free_only and is_free)):
                    continue
            out.append(dict(r))
        return out
