"""
SyncService - Pure Business Logic Layer untuk Sinkronisasi
Tidak memiliki dependency terhadap UI (QWidget/QMessageBox)
Digunakan oleh SyncFlowController untuk mengelola alur sinkronisasi
"""

import logging
import threading
from PySide6.QtCore import QObject, Signal, QThread
from pypos.modules.sinkronisasi.workers.sinkron_thread import SinkronWorker
from pypos.modules.sinkronisasi.services.export_cycle_service import ExportCycleService
from pypos.modules.sinkronisasi.config.sync_config import (
    get_sync_tables as _get_sync_tables_cfg,
    get_master_tables as _get_master_tables_cfg,
    get_sync_cleanup_wait_timeout_ms,
    get_sync_stop_wait_timeout_ms,
)

LOGGER = logging.getLogger(__name__)

def _load_sync_tables():
    return _get_sync_tables_cfg()

def get_sync_tables():
    return _load_sync_tables()

def _load_master_tables():
    return _get_master_tables_cfg()

def get_master_tables():
    return _load_master_tables()


class SyncService(QObject):
    """
    Service layer untuk sinkronisasi data
    Bertanggung jawab hanya untuk business logic tanpa UI dependency
    """
    progress_updated = Signal(int, str, str)  # (percent, status, detail_log)
    sync_completed = Signal(int)              # total_rows updated
    sync_failed = Signal(str)                 # Error message

    def __init__(self):
        super().__init__()
        self.thread = None
        self.worker = None
        self.tables = _load_sync_tables()
        # edited by glg
        # Guard konkurensi agar tidak ada thread sinkronisasi dobel.
        self._sync_lock = threading.Lock()
        self._sync_running = False

    def start_sync(self, tables=None, force_full_tables=None, full_refresh_tables=None):
        """
        Memulai proses sinkronisasi menggunakan QThread
        Method ini menggunakan logika yang sama dengan SinkronController.mulai_sinkron()
        """
        with self._sync_lock:
            if ExportCycleService.is_running():
                LOGGER.warning(
                    "[SyncService] Permintaan sinkron diabaikan: export transaksi sedang berjalan."
                )
                return False
            already_running = self._sync_running or (
                self.thread is not None and self.thread.isRunning()
            )
            if already_running:
                LOGGER.warning(
                    "[SyncService] Permintaan sinkron diabaikan: proses sinkronisasi masih berjalan."
                )
                return False
            self._sync_running = True

        try:
            # Buat thread dan worker baru
            self.thread = QThread()
            table_list = tables if tables is not None else self.tables
            self.worker = SinkronWorker(
                table_list,
                force_full_tables=force_full_tables,
                full_refresh_tables=full_refresh_tables
            )
            self.worker.moveToThread(self.thread)

            # Hubungkan signals dari worker ke service signals
            self.thread.started.connect(self.worker.run)
            self.worker.progress.connect(self._handle_progress)
            self.worker.selesai.connect(self._handle_completion)
            self.worker.gagal.connect(self._handle_failure)

            # Cleanup: otomatis setelah selesai
            self.worker.selesai.connect(self._cleanup_thread)
            self.worker.gagal.connect(self._cleanup_thread)

            # Mulai thread
            self.thread.start()
            LOGGER.info("[SyncService] Sinkronisasi dimulai")
            return True
        except (RuntimeError, TypeError, ValueError, AttributeError):
            with self._sync_lock:
                self._sync_running = False
            raise

    def _handle_progress(self, percent, status, detail_log):
        """
        Internal handler untuk progress update dari worker
        Args:
            percent (int): Progress percentage (0-100)
            status (str): Status message
            detail_log (str): Detail log message
        """
        LOGGER.debug("[SyncService] Progress: %s%% - %s", percent, status)
        self.progress_updated.emit(percent, status, detail_log)

    def _handle_completion(self, total_rows):
        """
        Internal handler untuk completion dari worker
        Args:
            total_rows (int): Total rows yang berhasil disinkronkan
        """
        LOGGER.info("[SyncService] Sinkronisasi selesai: %s rows updated", total_rows)
        self.sync_completed.emit(total_rows)

    def _handle_failure(self, error_message):
        """
        Internal handler untuk failure dari worker
        """
        LOGGER.error("[SyncService] Sinkronisasi gagal: %s", error_message)
        self.sync_failed.emit(error_message)

    def _cleanup_thread(self):
        """
        Cleanup thread dan worker setelah selesai
        Menggunakan logika yang sama dengan SinkronController.cleanup_thread()
        """
        if self.thread and self.thread.isRunning():
            wait_timeout_ms = get_sync_cleanup_wait_timeout_ms()
            self.thread.quit()
            self.thread.wait(wait_timeout_ms)
            if self.thread.isRunning():
                LOGGER.warning("[SyncService] Thread cleanup timeout; defer cleanup until thread stops.")
                return

        # Hapus referensi
        if self.worker:
            self.worker.deleteLater()
            self.worker = None

        if self.thread:
            self.thread.deleteLater()
            self.thread = None

        with self._sync_lock:
            self._sync_running = False

        LOGGER.debug("[SyncService] Thread cleanup selesai")

    def is_running(self):
        """
        Cek apakah sinkronisasi sedang berjalan
        """
        return self.thread is not None and self.thread.isRunning()

    def stop_sync(self, force=False, wait_timeout_ms=None):
        """
        Hentikan proses sinkronisasi jika sedang berjalan (graceful).
        """
        if self.is_running():
            LOGGER.info("[SyncService] Menghentikan sinkronisasi...")
            if wait_timeout_ms is None:
                wait_timeout_ms = get_sync_stop_wait_timeout_ms()
            if self.thread:
                try:
                    self.thread.requestInterruption()
                except (RuntimeError, AttributeError) as exc:
                    LOGGER.debug("[SyncService] requestInterruption gagal: %s", exc)
                self.thread.quit()
                self.thread.wait(wait_timeout_ms)
                if force and self.thread.isRunning():
                    LOGGER.warning(
                        "[SyncService] Sync thread masih berjalan setelah timeout; terminate dinonaktifkan."
                    )
            self._cleanup_thread()
