"""Tests for session recording and playback."""

import json
import os
import tempfile
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from browser_hybrid import Browser
from browser_hybrid.models import Action, Recording


class TestRecording:
    def test_start_stop_recording(self):
        """Test basic recording lifecycle."""
        with tempfile.TemporaryDirectory() as tmpdir:
            path = os.path.join(tmpdir, "test.json")
            browser = Browser()

            browser.start_recording(path)
            browser.stop_recording()

            assert os.path.exists(path)
            with open(path) as f:
                data = json.load(f)
                assert data["version"] == "1.0"
                assert "actions" in data
                assert "metadata" in data

    def test_record_goto(self):
        """Test goto action is recorded."""
        with tempfile.TemporaryDirectory() as tmpdir:
            path = os.path.join(tmpdir, "test.json")
            browser = Browser()

            # Mock new_tab and wait_for_load_state since they are called by goto
            with patch.object(browser, "new_tab"), patch.object(browser, "wait_for_load_state"):
                browser.start_recording(path)
                browser.goto("https://example.com")
                browser.stop_recording()

            assert os.path.exists(path)
            with open(path) as f:
                data = json.load(f)
                assert len(data["actions"]) == 1
                assert data["actions"][0]["type"] == "goto"
                assert data["actions"][0]["url"] == "https://example.com"

    def test_empty_recording(self):
        """Test recording with no actions."""
        browser = Browser()
        browser.start_recording()
        path = browser.stop_recording()

        assert os.path.exists(path)
        with open(path) as f:
            data = json.load(f)
            assert data["actions"] == []

        if os.path.exists(path):
            os.remove(path)

    def test_playback_success(self):
        """Test playback completes without errors."""
        browser = Browser()

        # Create a mock recording
        action1 = Action(type="goto", timestamp=0.1, params={"url": "https://test.com"})
        recording = Recording(actions=[action1])

        with patch.object(browser, "goto") as mock_goto:
            success = browser.playback(recording)
            assert success is True
            mock_goto.assert_called_once_with(url="https://test.com")

    def test_playback_verify_failure(self):
        """Test playback with verify=True raises on failure."""
        browser = Browser()

        # Action that returns False (failure)
        action = Action(type="click", timestamp=0.1, params={"ref": "axnode@1"})
        recording = Recording(actions=[action])

        from browser_hybrid.errors import BrowserError

        with patch.object(browser, "click", return_value=False):
            with pytest.raises(BrowserError, match="Action 'click' failed"):
                browser.playback(recording, verify=True)

    def test_playback_from_file(self):
        """Test playback from a JSON file."""
        with tempfile.TemporaryDirectory() as tmpdir:
            path = os.path.join(tmpdir, "playback.json")
            data = {
                "version": "1.0",
                "actions": [{"type": "goto", "url": "https://example.com", "timestamp": 0.0}],
            }
            with open(path, "w") as f:
                json.dump(data, f)

            browser = Browser()
            with patch.object(browser, "goto") as mock_goto:
                browser.playback(path)
                mock_goto.assert_called_once_with(url="https://example.com")

    def test_pause_resume_recording(self):
        """Test pausing and resuming recording."""
        browser = Browser()
        browser.start_recording()

        with patch.object(browser, "new_tab"), patch.object(browser, "wait_for_load_state"):
            browser.goto("https://recorded.com")
            browser.pause_recording()
            browser.goto("https://not-recorded.com")
            browser.resume_recording()
            browser.goto("https://recorded-again.com")

        recording = browser.get_recording()
        assert len(recording.actions) == 2
        assert recording.actions[0].params["url"] == "https://recorded.com"
        assert recording.actions[1].params["url"] == "https://recorded-again.com"

        browser.stop_recording()

    def test_recording_goto_suppresses_internal(self):
        """goto() should record one 'goto' action, not internal wait_for_load_state calls."""
        with tempfile.TemporaryDirectory() as tmpdir:
            path = os.path.join(tmpdir, "test.json")
            browser = Browser()

            # Mock new_tab to return a dummy tab and _send_cdp to just succeed
            from browser_hybrid.models import Tab

            dummy_tab = Tab(
                id="1",
                url="https://example.com",
                title="Example",
                type="page",
                web_socket_debugger_url="ws://dummy",
            )

            async def mock_cdp(*args, **kwargs):
                return {"result": {"value": "complete"}, "objectId": "dummy"}

            async def mock_get_conn(*args, **kwargs):
                return MagicMock()

            with (
                patch.object(browser, "new_tab", return_value=dummy_tab),
                patch.object(browser, "_send_cdp", side_effect=mock_cdp),
                patch.object(browser, "_get_connection", side_effect=mock_get_conn),
            ):
                browser.start_recording(path)
                browser.goto("https://example.com")
                browser.stop_recording()

                with open(path) as f:
                    data = json.load(f)
                    actions = [a["type"] for a in data["actions"]]
                    assert actions == ["goto"]

    def test_recording_from_dict_no_path(self):
        """from_dict should result in path=None by default (machine-agnostic)."""
        from browser_hybrid.models import Action, Recording

        recording = Recording(
            path="/tmp/test.json", version="1.0", actions=[Action(type="click", timestamp=0.0)]
        )

        data = recording.to_dict()
        # path should NOT be in serialized data
        assert "path" not in data

        loaded = Recording.from_dict(data)
        # path should be None after from_dict (not serialized)
        assert loaded.path is None

    @patch("browser_hybrid.browser.Browser._send_cdp")
    @patch("browser_hybrid.browser.Browser._get_connection")
    def test_recording_from_file_path(self, mock_get_conn, mock_send_cdp):
        """from_file should set path after loading."""
        from browser_hybrid.models import Recording

        mock_get_conn.return_value = AsyncMock()
        mock_send_cdp.return_value = {"result": {"value": "complete"}}

        with tempfile.TemporaryDirectory() as tmpdir:
            path = os.path.join(tmpdir, "test_file.json")
            browser = Browser()
            browser.start_recording(path)
            browser.click("axnode@123")  # Use simple action instead of goto to avoid new_tab call
            returned_path = browser.stop_recording()

            recording = Recording.from_file(returned_path)
            assert recording.path == returned_path
            browser.close()
