from __future__ import annotations

import json
import unittest

from chaser_sdk.client import ChaserClient, HttpResponse


class FakeTransport:
    def __init__(self, responses: list[HttpResponse]) -> None:
        self.responses = list(responses)
        self.calls: list[dict[str, object]] = []

    def request(self, method: str, url: str, headers, body, timeout):
        self.calls.append(
            {
                "method": method,
                "url": url,
                "headers": dict(headers),
                "body": body,
                "timeout": timeout,
            }
        )
        return self.responses.pop(0)


class ChaserClientTests(unittest.TestCase):
    def test_create_session_sends_bearer_and_account_headers(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=json.dumps(
                        {
                            "id": "sess_123",
                            "created_at": "2026-03-06T00:00:00Z",
                            "status": "running",
                            "session_type": "sandbox",
                            "endpoints": {"control": "https://sess_123.example.test"},
                        }
                    ).encode("utf-8"),
                )
            ]
        )
        client = ChaserClient(
            base_url="https://api.example.test/",
            api_key="sk_test",
            account="Acme Engineering",
            transport=transport,
        )

        session = client.sessions.create({"workspace": "frontend-app", "session_type": "sandbox"})

        self.assertEqual(session["id"], "sess_123")
        call = transport.calls[0]
        self.assertEqual(call["url"], "https://api.example.test/v1/sessions")
        self.assertEqual(call["headers"]["Authorization"], "Bearer sk_test")
        self.assertEqual(call["headers"]["X-Chaser-Account"], "Acme Engineering")
        self.assertEqual(json.loads(call["body"].decode("utf-8")), {"workspace": "frontend-app", "session_type": "sandbox"})

    def test_upload_text_uses_multipart_form_data(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"path":"/workspace/demo.txt","size":5}',
                )
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        result = client.files.upload_text("sess_abc", "/workspace/demo.txt", "hello")

        self.assertEqual(result["size"], 5)
        call = transport.calls[0]
        self.assertEqual(call["url"], "https://api.example.test/v1/sessions/sess_abc/upload")
        self.assertIn("multipart/form-data", call["headers"]["Content-Type"])
        self.assertIn(b'name="path"', call["body"])
        self.assertIn(b"/workspace/demo.txt", call["body"])

    def test_wait_until_ready_polls_until_ready(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"id":"sess_ready","created_at":"2026-03-06T00:00:00Z","status":"creating","session_type":"browser","endpoints":{"control":"https://sess_ready.example.test"}}',
                ),
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"id":"sess_ready","created_at":"2026-03-06T00:00:00Z","status":"ready","session_type":"browser","endpoints":{"control":"https://sess_ready.example.test"}}',
                ),
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        session = client.sessions.wait_until_ready("sess_ready", timeout_secs=1.0, interval_secs=0.0)

        self.assertEqual(session["status"], "ready")
        self.assertEqual(len(transport.calls), 2)

    def test_sessions_list_accepts_paginated_envelope(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"sessions":[{"id":"sess_1","created_at":"2026-03-06T00:00:00Z","status":"ready","session_type":"sandbox","endpoints":{"control":"https://sess_1.example.test"}}],"total":1}',
                ),
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"sessions":[{"id":"sess_1","created_at":"2026-03-06T00:00:00Z","status":"ready","session_type":"sandbox","endpoints":{"control":"https://sess_1.example.test"}}],"total":1}',
                ),
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        sessions = client.sessions.list()
        page = client.sessions.list_page()

        self.assertEqual(len(sessions), 1)
        self.assertEqual(page["total"], 1)
        self.assertEqual(page["sessions"][0]["id"], "sess_1")

    def test_create_session_supports_github_repo_bootstrap(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"id":"sess_repo","created_at":"2026-03-06T00:00:00Z","status":"creating","session_type":"sandbox","endpoints":{"control":"https://sess_repo.example.test"}}',
                )
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        client.sessions.create(
            {
                "workspace": "frontend-app",
                "session_type": "sandbox",
                "github_repo": "acme/frontend-app",
            }
        )

        call = transport.calls[0]
        self.assertEqual(
            json.loads(call["body"].decode("utf-8")),
            {
                "workspace": "frontend-app",
                "session_type": "sandbox",
                "github_repo": "acme/frontend-app",
            },
        )

    def test_session_self_test_posts_to_runtime_diagnostic_endpoint(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=json.dumps(
                        {
                            "kind": "sandbox.self_test",
                            "status": "ok",
                            "session_id": "sess_diag",
                            "workspace_id": "ws_diag",
                            "runtime": {
                                "cwd": "/root",
                                "home": "/root",
                                "user": "root",
                                "shell": "/bin/sh",
                                "term": "dumb",
                                "lang": "C.UTF-8",
                                "chaser_mode": "exec",
                                "chaser_exec": True,
                                "chaser_agent": False,
                                "workspace_root_present": True,
                                "path_entries": [
                                    "/usr/local/sbin",
                                    "/usr/local/bin",
                                    "/usr/sbin",
                                    "/usr/bin",
                                    "/sbin",
                                    "/bin",
                                ],
                                "proxy": {
                                    "all_proxy": "socks5h://127.0.0.1:1080",
                                    "http_proxy": None,
                                    "https_proxy": None,
                                    "no_proxy": "127.0.0.1,localhost",
                                },
                                "tools": {
                                    "node": "/opt/chaser/bin/node",
                                    "python3": "/usr/bin/python3",
                                    "git": "/usr/bin/git",
                                },
                            },
                        }
                    ).encode("utf-8"),
                )
            ]
        )
        client = ChaserClient(
            base_url="https://api.example.test/",
            api_key="sk_test",
            account="Acme Engineering",
            transport=transport,
        )

        diagnostics = client.sessions.self_test("sess_diag")

        self.assertEqual(diagnostics["status"], "ok")
        self.assertEqual(diagnostics["runtime"]["cwd"], "/root")
        self.assertEqual(diagnostics["runtime"]["tools"]["node"], "/opt/chaser/bin/node")
        call = transport.calls[0]
        self.assertEqual(call["url"], "https://api.example.test/v1/sessions/sess_diag/self-test")
        self.assertEqual(call["method"], "POST")
        self.assertEqual(call["headers"]["Authorization"], "Bearer sk_test")
        self.assertEqual(call["headers"]["X-Chaser-Account"], "Acme Engineering")

    def test_browser_cdp_websocket_url_uses_json_version(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"id":"browser_1","created_at":"2026-03-06T00:00:00Z","status":"ready","session_type":"browser","endpoints":{"control":"https://browser_1.example.test"}}',
                ),
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"webSocketDebuggerUrl":"wss://browser_1.example.test/devtools/browser/abc"}',
                ),
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        websocket_url = client.browser.cdp_websocket_url("browser_1")

        self.assertEqual(websocket_url, "wss://browser_1.example.test/devtools/browser/abc")

    def test_github_list_repositories_returns_install_url(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"repositories":[{"name":"acme/frontend-app","installation_id":42,"account_login":"acme"}],"install_url":"https://github.com/apps/chaser/installations/new"}',
                )
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        response = client.github.list_repositories()

        self.assertEqual(response["repositories"][0]["name"], "acme/frontend-app")
        self.assertEqual(
            response["install_url"], "https://github.com/apps/chaser/installations/new"
        )

    def test_workspaces_import_from_github_posts_repo_payload(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"workspace":{"id":"ws_123","name":"frontend-app","created_at":"2026-03-06T00:00:00Z","persistence":true,"template":false},"github_repo":"acme/frontend-app","private":true}',
                )
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        imported = client.workspaces.import_from_github(
            {"repo": "acme/frontend-app", "name": "frontend-app"}
        )

        self.assertEqual(imported["workspace"]["id"], "ws_123")
        call = transport.calls[0]
        self.assertEqual(call["url"], "https://api.example.test/v1/workspaces/import")
        self.assertEqual(
            json.loads(call["body"].decode("utf-8")),
            {"repo": "acme/frontend-app", "name": "frontend-app"},
        )

    def test_jobs_pipeline_helper_creates_pipeline_run_job(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"id":"job_pipeline","kind":"pipeline.run","status":"queued","input":{"workspace":"frontend-app","steps":[{"command":"npm ci"},{"command":"npm test"}]},"created_at":"2026-03-06T00:00:00Z","updated_at":"2026-03-06T00:00:00Z"}',
                )
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        job = client.jobs.pipeline(
            {
                "workspace": "frontend-app",
                "steps": [{"command": "npm ci"}, {"command": "npm test"}],
            },
            idempotency_key="pipe-1",
        )

        self.assertEqual(job["kind"], "pipeline.run")
        call = transport.calls[0]
        self.assertEqual(call["url"], "https://api.example.test/v1/jobs")
        self.assertEqual(
            json.loads(call["body"].decode("utf-8")),
            {
                "kind": "pipeline.run",
                "input": {
                    "workspace": "frontend-app",
                    "steps": [{"command": "npm ci"}, {"command": "npm test"}],
                },
                "idempotency_key": "pipe-1",
            },
        )

    def test_jobs_github_runner_helper_creates_runner_job(self) -> None:
        transport = FakeTransport(
            [
                HttpResponse(
                    status=200,
                    headers={"content-type": "application/json"},
                    body=b'{"id":"job_runner","kind":"github.runner","status":"queued","input":{"repo":"acme/frontend-app","installation_id":42,"workflow_job_id":99,"labels":["self-hosted","linux","x64","chaser"]},"created_at":"2026-03-06T00:00:00Z","updated_at":"2026-03-06T00:00:00Z"}',
                )
            ]
        )
        client = ChaserClient(base_url="https://api.example.test", transport=transport)

        job = client.jobs.github_runner(
            {
                "repo": "acme/frontend-app",
                "installation_id": 42,
                "workflow_job_id": 99,
                "labels": ["self-hosted", "linux", "x64", "chaser"],
            }
        )

        self.assertEqual(job["kind"], "github.runner")
        call = transport.calls[0]
        self.assertEqual(
            json.loads(call["body"].decode("utf-8")),
            {
                "kind": "github.runner",
                "input": {
                    "repo": "acme/frontend-app",
                    "installation_id": 42,
                    "workflow_job_id": 99,
                    "labels": ["self-hosted", "linux", "x64", "chaser"],
                },
            },
        )


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