import threading
import unittest

from pypos.modules.sinkronisasi.services.export_cycle_service import ExportCycleService


class _ExportServiceStub:
    def __init__(self, wait_event=None, done_event=None, rows=1):
        self.wait_event = wait_event
        self.done_event = done_event
        self.rows = int(rows)
        self.calls = 0

    def export_batch(self):
        self.calls += 1
        if self.wait_event is not None:
            self.wait_event.wait(timeout=2.0)
        if self.done_event is not None:
            self.done_event.set()
        return self.rows


class _UploadServiceStub:
    def __init__(self, uploaded=1):
        self.uploaded = int(uploaded)
        self.calls = 0
        self.last_kwargs = {}

    def process_pending_uploads(self, **kwargs):
        self.calls += 1
        self.last_kwargs = dict(kwargs or {})
        return {
            "uploaded": self.uploaded,
            "failed": 0,
            "retried": 0,
            "cleaned_files": 0,
            "cleaned_empty_flux": 0,
        }


class ExportCycleServiceTests(unittest.TestCase):
    def test_run_cycle_singleton_guard_prevents_overlap(self):
        wait_event = threading.Event()
        done_event = threading.Event()

        export_a = _ExportServiceStub(wait_event=wait_event, done_event=done_event, rows=3)
        upload_a = _UploadServiceStub(uploaded=2)
        service_a = ExportCycleService(export_service=export_a, upload_service=upload_a)

        results = {}

        def _run_first():
            results["first"] = service_a.run_cycle(source="timer")

        thread = threading.Thread(target=_run_first, daemon=True)
        thread.start()

        done_event.wait(timeout=1.0)

        export_b = _ExportServiceStub(rows=10)
        upload_b = _UploadServiceStub(uploaded=10)
        service_b = ExportCycleService(export_service=export_b, upload_service=upload_b)
        results["second"] = service_b.run_cycle(source="shutdown")

        wait_event.set()
        thread.join(timeout=2.0)

        self.assertTrue(results["first"]["ran"])
        self.assertFalse(results["second"]["ran"])
        self.assertEqual(results["second"]["skipped"], "already_running")
        self.assertEqual(export_a.calls, 1)
        self.assertEqual(export_b.calls, 0)
        self.assertEqual(upload_b.calls, 0)

    def test_run_cycle_returns_aggregate_result(self):
        service = ExportCycleService(
            export_service=_ExportServiceStub(rows=7),
            upload_service=_UploadServiceStub(uploaded=5),
        )
        result = service.run_cycle(source="manual")
        self.assertTrue(result["ran"])
        self.assertEqual(result["exported_rows"], 7)
        self.assertEqual(result["uploaded"], 5)
        self.assertEqual(result["failed"], 0)
        self.assertEqual(result["retried"], 0)

    def test_run_cycle_skip_upload_does_not_call_upload_service(self):
        # edited by glg
        upload_service = _UploadServiceStub(uploaded=9)
        service = ExportCycleService(
            export_service=_ExportServiceStub(rows=4),
            upload_service=upload_service,
        )
        result = service.run_cycle(source="app_close", skip_upload=True)
        self.assertTrue(result["ran"])
        self.assertEqual(result["exported_rows"], 4)
        self.assertEqual(result["uploaded"], 0)
        self.assertEqual(result["skipped_upload"], "skip_upload_requested")
        self.assertEqual(upload_service.calls, 0)

    def test_run_cycle_forwards_upload_overrides(self):
        # edited by glg
        upload_service = _UploadServiceStub(uploaded=3)
        service = ExportCycleService(
            export_service=_ExportServiceStub(rows=2),
            upload_service=upload_service,
        )
        result = service.run_cycle(
            source="startup_replay",
            upload_limit=5,
            requeue_failed_transient=True,
            upload_timeout_override=7,
        )
        self.assertTrue(result["ran"])
        self.assertEqual(result["uploaded"], 3)
        self.assertEqual(upload_service.calls, 1)
        self.assertEqual(upload_service.last_kwargs.get("limit_override"), 5)
        self.assertTrue(upload_service.last_kwargs.get("requeue_failed_transient"))
        self.assertEqual(upload_service.last_kwargs.get("timeout_override"), 7)


if __name__ == "__main__":
    unittest.main()
