import unittest

from tests.support.harness import TmuxOnlyTestCase


class WorkspaceLeaseTests(TmuxOnlyTestCase):
    def test_status_and_list_surface_consistent_lease_fields(self):
        sid = self.server.pty_spawn(
            "sleep 30",
            owner="surface-owner",
            scope="read-write",
            backend="tmux",
            name="surface-basic",
        )

        self.server.pty_attach(sid, "client-a")
        self.server.pty_handoff(
            sid,
            client_id="client-a",
            target_client_id="client-b",
            reason="surface handoff",
        )

        status = self.server.pty_status(sid)
        listed = {
            item["session_id"]: item
            for item in self.server.pty_list(owner="surface-owner")
        }[sid]

        for key in [
            "attached",
            "attached_client_id",
            "attached_at",
            "detached_at",
            "last_lease_at",
            "last_lease_event",
            "last_lease_from_client_id",
            "last_lease_to_client_id",
            "last_lease_reason",
            "recovered",
            "recovered_at",
            "workspace_attached",
            "workspace_attached_client_id",
            "workspace_attached_session_count",
        ]:
            self.assertEqual(status[key], listed[key], key)

        self.assertEqual(status["last_lease_event"], "handoff")
        self.assertEqual(status["last_lease_from_client_id"], "client-a")
        self.assertEqual(status["last_lease_to_client_id"], "client-b")
        self.assertEqual(status["last_lease_reason"], "surface handoff")

    def test_attach_detach_basic_flow(self):
        sid = self.server.pty_spawn(
            "sleep 30",
            owner="lease-owner",
            scope="read-write",
            backend="tmux",
            name="lease-basic",
        )

        attached = self.server.pty_attach(sid, "client-a")
        self.assertTrue(attached["attached"])
        self.assertEqual(attached["attached_client_id"], "client-a")
        self.assertEqual(attached["last_lease_event"], "attach")

        detached = self.server.pty_detach(sid, "client-a")
        self.assertFalse(detached["attached"])
        self.assertIsNone(detached["attached_client_id"])
        self.assertEqual(detached["last_lease_event"], "detach")

    def test_attach_is_idempotent_for_same_client(self):
        sid = self.server.pty_spawn(
            "sleep 30",
            owner="idempotent-owner",
            scope="read-write",
            backend="tmux",
            name="idempotent-attach",
        )

        first = self.server.pty_attach(sid, "client-a")
        second = self.server.pty_attach(sid, "client-a")

        self.assertTrue(first["attach_changed"])
        self.assertFalse(second["attach_changed"])
        self.assertEqual(second["attached_client_id"], "client-a")
        self.assertEqual(second["last_lease_event"], "attach")

    def test_detach_is_idempotent_for_same_client(self):
        sid = self.server.pty_spawn(
            "sleep 30",
            owner="idempotent-owner",
            scope="read-write",
            backend="tmux",
            name="idempotent-detach",
        )

        self.server.pty_attach(sid, "client-a")
        first = self.server.pty_detach(sid, "client-a")
        second = self.server.pty_detach(sid, "client-a")

        self.assertTrue(first["detach_changed"])
        self.assertFalse(second["detach_changed"])
        self.assertFalse(second["attached"])
        self.assertIsNone(second["attached_client_id"])
        self.assertEqual(second["last_lease_event"], "detach")

    def test_attached_session_rejects_other_client_write(self):
        sid = self.server.pty_spawn(
            "read line; printf 'got:%s' \"$line\"",
            owner="lease-owner",
            scope="read-write",
            backend="tmux",
            name="lease-write",
        )
        self.server.pty_attach(sid, "client-a")

        with self.assertRaises(RuntimeError) as ctx:
            self.server.pty_write(sid, "bad\n", client_id="client-b")

        self.assertIn("session is attached to another client", str(ctx.exception))

    def test_handoff_transfers_lease(self):
        sid = self.server.pty_spawn(
            "read line; printf 'got:%s' \"$line\"",
            owner="handoff-owner",
            scope="read-write",
            backend="tmux",
            name="handoff-one",
        )
        self.server.pty_attach(sid, "client-a")

        handed = self.server.pty_handoff(
            sid,
            client_id="client-a",
            target_client_id="client-b",
            reason="operator handoff",
        )
        self.assertEqual(handed["attached_client_id"], "client-b")
        self.assertEqual(handed["last_lease_event"], "handoff")
        self.assertEqual(handed["last_lease_from_client_id"], "client-a")
        self.assertEqual(handed["last_lease_to_client_id"], "client-b")
        self.assertEqual(handed["last_lease_reason"], "operator handoff")

    def test_takeover_transfers_lease(self):
        sid = self.server.pty_spawn(
            "read line; printf 'got:%s' \"$line\"",
            owner="takeover-owner",
            scope="read-write",
            backend="tmux",
            name="takeover-one",
        )
        self.server.pty_attach(sid, "client-a")

        taken = self.server.pty_takeover(
            sid,
            client_id="client-c",
            from_client_id="client-a",
            reason="controller resume",
        )
        self.assertEqual(taken["attached_client_id"], "client-c")
        self.assertEqual(taken["last_lease_event"], "takeover")
        self.assertEqual(taken["last_lease_from_client_id"], "client-a")
        self.assertEqual(taken["last_lease_to_client_id"], "client-c")
        self.assertEqual(taken["last_lease_reason"], "controller resume")

    def test_workspace_requires_single_client_id(self):
        sid_a = self.server.pty_spawn(
            "sleep 30",
            owner="ws-owner",
            scope="read-write",
            backend="tmux",
            tmux_workspace="lease-ws",
            name="ws-a",
        )
        sid_b = self.server.pty_spawn(
            "sleep 30",
            owner="ws-owner",
            scope="read-write",
            backend="tmux",
            tmux_workspace="lease-ws",
            name="ws-b",
        )

        self.server.pty_attach(sid_a, "client-a")
        with self.assertRaises(RuntimeError) as ctx:
            self.server.pty_attach(sid_b, "client-b")

        self.assertIn(
            "tmux workspace is attached to another client", str(ctx.exception)
        )

    def test_workspace_handoff_moves_all_attached_members(self):
        sid_a = self.server.pty_spawn(
            "sleep 30",
            owner="ws-owner",
            scope="read-write",
            backend="tmux",
            tmux_workspace="handoff-ws",
            name="handoff-a",
        )
        sid_b = self.server.pty_spawn(
            "sleep 30",
            owner="ws-owner",
            scope="read-write",
            backend="tmux",
            tmux_workspace="handoff-ws",
            name="handoff-b",
        )

        self.server.pty_attach(sid_a, "client-a")
        self.server.pty_attach(sid_b, "client-a")

        self.server.pty_handoff(
            sid_a,
            client_id="client-a",
            target_client_id="client-b",
            reason="workspace handoff",
        )

        status_a = self.server.pty_status(sid_a)
        status_b = self.server.pty_status(sid_b)
        self.assertEqual(status_a["attached_client_id"], "client-b")
        self.assertEqual(status_b["attached_client_id"], "client-b")
        self.assertEqual(status_a["workspace_attached_client_id"], "client-b")
        self.assertEqual(status_b["workspace_attached_client_id"], "client-b")
        self.assertEqual(status_a["workspace_attached_session_count"], 2)
        self.assertEqual(status_b["workspace_attached_session_count"], 2)

    def test_resume_stays_live_only_after_session_removed(self):
        sid = self.server.pty_spawn(
            "sleep 30",
            owner="resume-boundary-owner",
            scope="read-write",
            backend="tmux",
            name="resume-boundary",
        )

        session = self.server.sessions.pop(sid)
        try:
            with self.assertRaises(RuntimeError) as ctx:
                self.server.pty_resume("resume-boundary", owner="resume-boundary-owner")
        finally:
            self.server._cleanup_session(sid, session)

        self.assertIn("unknown session name: resume-boundary", str(ctx.exception))


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