"""Playwright-based unit tests for Studio JavaScript path-utility functions.

These tests load the Studio UI in a headless Chromium browser, then call the
pure JS utility functions exposed via ``window.__studioUtils`` (only enabled
when ``?__test_utils=1`` is appended to the URL).

Why test JS utilities via pytest-playwright instead of extracting them to a
separate JS module?
- The functions live inside the Vue ``setup()`` closure and reference reactive
  state (``studioTargetPath``, ``studioWorkspaceRoot``).  Keeping them in place
  avoids a large refactor.
- pytest + Playwright gives us real browser execution without a separate Node.js
  test infrastructure.
- Bugs like the "prompt yaml dropdown appears empty" (#v0.6.9) are caused by
  incorrect path transformations in these functions, so direct testing closes
  the feedback loop immediately.

Fixtures (defined in conftest.py):
    studio_utils_page: ``(page, base_url)`` – Studio already loaded with
        ``?__test_utils=1``, Vue mounted, ``window.__studioUtils`` available.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

import pytest

if TYPE_CHECKING:
    from playwright.sync_api import Page


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _u(page: Page, fn: str, *args: Any) -> Any:
    """Call ``window.__studioUtils.<fn>`` with positional *args* and return the result."""
    js_args = ", ".join(
        f'"{a}"' if isinstance(a, str) else ("null" if a is None else str(a)) for a in args
    )
    return page.evaluate(f"window.__studioUtils.{fn}({js_args})")


# ---------------------------------------------------------------------------
# normalizePosixPath
# ---------------------------------------------------------------------------


class TestNormalizePosixPath:
    def test_empty_string(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "normalizePosixPath", "") == ""

    def test_simple_path(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "normalizePosixPath", "prompts/foo.yaml") == "prompts/foo.yaml"

    def test_removes_trailing_slash(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "normalizePosixPath", "prompts/foo/") == "prompts/foo"

    def test_collapses_double_slashes(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "normalizePosixPath", "a//b//c.yaml") == "a/b/c.yaml"

    def test_resolves_dot_dot(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "normalizePosixPath", "a/b/../c.yaml") == "a/c.yaml"

    def test_windows_backslash_converted(self, studio_utils_page):
        page, _ = studio_utils_page
        # Pass the string via page.evaluate to avoid Python escape-sequence interpretation.
        result = page.evaluate(r'window.__studioUtils.normalizePosixPath("prompts\\foo.yaml")')
        assert result == "prompts/foo.yaml"


# ---------------------------------------------------------------------------
# joinPosixPath
# ---------------------------------------------------------------------------


class TestJoinPosixPath:
    def test_basic_join(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "joinPosixPath", "a/b", "c.yaml") == "a/b/c.yaml"

    def test_empty_base_returns_child(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "joinPosixPath", "", "c.yaml") == "c.yaml"

    def test_empty_child_returns_base(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "joinPosixPath", "a/b", "") == "a/b"

    def test_absolute_child_overrides_base(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "joinPosixPath", "a/b", "/abs/path.yaml") == "/abs/path.yaml"


# ---------------------------------------------------------------------------
# relativePosixPath
# ---------------------------------------------------------------------------


class TestRelativePosixPath:
    def test_sibling_directory(self, studio_utils_page):
        page, _ = studio_utils_page
        # sub_workflow → sub_workflow/prompts/foo.yaml
        assert (
            _u(page, "relativePosixPath", "sub_workflow", "sub_workflow/prompts/foo.yaml")
            == "prompts/foo.yaml"
        )

    def test_same_directory(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "relativePosixPath", "a/b", "a/b/c.yaml") == "c.yaml"

    def test_parent_escape(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "relativePosixPath", "a/b", "other/c.yaml") == "../../other/c.yaml"


# ---------------------------------------------------------------------------
# splitPromptReference
# ---------------------------------------------------------------------------


class TestSplitPromptReference:
    def test_with_hash(self, studio_utils_page):
        page, _ = studio_utils_page
        result = page.evaluate(
            'window.__studioUtils.splitPromptReference("prompts/foo.yaml#my_key")'
        )
        assert result["path"] == "prompts/foo.yaml"
        assert result["keyPath"] == "my_key"

    def test_without_hash(self, studio_utils_page):
        page, _ = studio_utils_page
        result = page.evaluate('window.__studioUtils.splitPromptReference("prompts/foo.yaml")')
        assert result["path"] == "prompts/foo.yaml"
        assert result["keyPath"] == ""

    def test_empty_string(self, studio_utils_page):
        page, _ = studio_utils_page
        result = page.evaluate('window.__studioUtils.splitPromptReference("")')
        assert result["path"] == ""
        assert result["keyPath"] == ""


# ---------------------------------------------------------------------------
# getWorkflowDirectoryRelative
# ---------------------------------------------------------------------------


class TestGetWorkflowDirectoryRelative:
    """``studioTargetPath`` is an absolute path; the function strips the workspace prefix.

    Returns the directory portion as a workspace-root-relative string.
    """

    def test_returns_subdirectory_name(self, studio_utils_page):
        """Workflow in sub_workflow/ → returns 'sub_workflow'."""
        page, _ = studio_utils_page
        result = _u(page, "getWorkflowDirectoryRelative")
        assert result == "sub_workflow"

    def test_returns_empty_when_workflow_at_root(self, studio_utils_page_root_level):
        """Workflow directly under workspace root → returns ''."""
        page, _ = studio_utils_page_root_level
        result = _u(page, "getWorkflowDirectoryRelative")
        assert result == ""


# ---------------------------------------------------------------------------
# promptRefPathToWorkspacePath  ← 今回バグがあった関数
# ---------------------------------------------------------------------------


class TestPromptRefPathToWorkspacePath:
    """The workflow is at ``sub_workflow/workflow.yaml`` inside the workspace.

    ``getWorkflowDirectoryRelative()`` should return ``"sub_workflow"``.
    All relative ``prompt_ref`` paths are resolved against that directory.
    """

    def test_bare_relative_path_resolves_against_workflow_dir(self, studio_utils_page):
        """Core regression: 'prompts/foo.yaml' must become 'sub_workflow/prompts/foo.yaml'."""
        page, _ = studio_utils_page
        result = _u(page, "promptRefPathToWorkspacePath", "prompts/foo.yaml")
        assert result == "sub_workflow/prompts/foo.yaml"

    def test_explicit_dot_slash(self, studio_utils_page):
        page, _ = studio_utils_page
        result = _u(page, "promptRefPathToWorkspacePath", "./prompts/foo.yaml")
        assert result == "sub_workflow/prompts/foo.yaml"

    def test_explicit_dot_dot(self, studio_utils_page):
        """'../sibling/foo.yaml' from sub_workflow → 'sibling/foo.yaml' in workspace."""
        page, _ = studio_utils_page
        result = _u(page, "promptRefPathToWorkspacePath", "../sibling/foo.yaml")
        assert result == "sibling/foo.yaml"

    def test_empty_returns_empty(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "promptRefPathToWorkspacePath", "") == ""

    def test_already_workspace_relative(self, studio_utils_page):
        """A path that already starts with the workflow directory stays correct."""
        page, _ = studio_utils_page
        result = _u(page, "promptRefPathToWorkspacePath", "sub_workflow/prompts/foo.yaml")
        # joinPosixPath("sub_workflow", "sub_workflow/prompts/foo.yaml")
        # The result includes the dir prefix; this documents current behaviour.
        assert "prompts/foo.yaml" in result

    def test_bare_relative_when_workflow_at_root(self, studio_utils_page_root_level):
        """Workflow at root: 'prompts/foo.yaml' stays 'prompts/foo.yaml' (workflowDir='')."""
        page, _ = studio_utils_page_root_level
        result = _u(page, "promptRefPathToWorkspacePath", "prompts/foo.yaml")
        assert result == "prompts/foo.yaml"

    def test_absolute_path_stripped_to_workspace_relative(self, studio_utils_page):
        """An absolute prompt_ref is converted to workspace-root-relative."""
        page, _ = studio_utils_page
        # Use page.evaluate directly to build the absolute path dynamically.
        result = page.evaluate("""
            (() => {
                const root = window.studioState?.workspaceRoot
                    || document.querySelector('[data-workspace-root]')?.dataset.workspaceRoot
                    || '';
                // Build a synthetic absolute path matching studioWorkspaceRoot.
                const ws = window.__studioUtils.normalizePosixPath(
                    window.__studioWorkspaceRoot || ''
                );
                if (!ws) return '__skipped__';
                return window.__studioUtils.promptRefPathToWorkspacePath(
                    ws + '/sub_workflow/prompts/foo.yaml'
                );
            })()
        """)
        # If we couldn't determine the workspace root, skip gracefully.
        if result == "__skipped__":
            pytest.skip("workspace root not accessible from page context")
        assert "prompts/foo.yaml" in result


# ---------------------------------------------------------------------------
# workspacePathToPromptRefPath  ← 今回バグがあった関数
# ---------------------------------------------------------------------------


class TestWorkspacePathToPromptRefPath:
    """Reverse of promptRefPathToWorkspacePath.

    workspace-root-relative ``sub_workflow/prompts/foo.yaml``
    should round-trip to the workflow-relative form ``prompts/foo.yaml``.
    """

    def test_workspace_relative_to_workflow_relative(self, studio_utils_page):
        """Core regression: stored prompt_ref should be workflow-directory-relative."""
        page, _ = studio_utils_page
        result = _u(page, "workspacePathToPromptRefPath", "sub_workflow/prompts/foo.yaml")
        assert result == "prompts/foo.yaml"

    def test_empty_returns_empty(self, studio_utils_page):
        page, _ = studio_utils_page
        assert _u(page, "workspacePathToPromptRefPath", "") == ""

    def test_path_in_sibling_workflow_dir_uses_relative_escape(self, studio_utils_page):
        """A path in a sibling directory must use '../' to escape the workflow dir."""
        page, _ = studio_utils_page
        result = _u(page, "workspacePathToPromptRefPath", "other_workflow/prompts/bar.yaml")
        assert result == "../other_workflow/prompts/bar.yaml"

    def test_path_at_workspace_root_escapes_workflow_dir(self, studio_utils_page):
        """A YAML at workspace root level (no subdir) gets '../' prefix."""
        page, _ = studio_utils_page
        result = _u(page, "workspacePathToPromptRefPath", "shared_prompts.yaml")
        assert result == "../shared_prompts.yaml"

    def test_workspace_relative_when_workflow_at_root(self, studio_utils_page_root_level):
        """Workflow at root: path is already workflow-relative, returned as-is."""
        page, _ = studio_utils_page_root_level
        result = _u(page, "workspacePathToPromptRefPath", "prompts/foo.yaml")
        assert result == "prompts/foo.yaml"

    def test_round_trip_standard(self, studio_utils_page):
        """prompt_ref -> workspace path -> prompt_ref should be identity."""
        page, _ = studio_utils_page
        original = "prompts/foo.yaml"
        workspace_path = _u(page, "promptRefPathToWorkspacePath", original)
        back = _u(page, "workspacePathToPromptRefPath", workspace_path)
        assert back == original

    def test_round_trip_nested(self, studio_utils_page):
        """Round-trip with a deeply nested prompt file."""
        page, _ = studio_utils_page
        original = "prompts/nested/deep/foo.yaml"
        workspace_path = _u(page, "promptRefPathToWorkspacePath", original)
        back = _u(page, "workspacePathToPromptRefPath", workspace_path)
        assert back == original

    def test_round_trip_when_workflow_at_root(self, studio_utils_page_root_level):
        """Round-trip works when the workflow is directly under the workspace root."""
        page, _ = studio_utils_page_root_level
        original = "prompts/foo.yaml"
        workspace_path = _u(page, "promptRefPathToWorkspacePath", original)
        back = _u(page, "workspacePathToPromptRefPath", workspace_path)
        assert back == original
