"""Integration tests for Prettier tool definition.

These tests require prettier to be installed (npm install -g prettier).
They verify the PrettierPlugin definition, check command, fix command, and set_options method.
"""

from __future__ import annotations

import shutil
from collections.abc import Callable
from pathlib import Path
from typing import TYPE_CHECKING

import pytest
from assertpy import assert_that

if TYPE_CHECKING:
    from lintro.plugins.base import BaseToolPlugin

# Skip all tests if prettier is not installed
pytestmark = pytest.mark.skipif(
    shutil.which("prettier") is None,
    reason="prettier not installed",
)


@pytest.fixture
def temp_js_file_unformatted(tmp_path: Path) -> str:
    """Create a temporary JavaScript file with formatting issues.

    Creates a file containing JavaScript code with formatting issues
    that Prettier should fix, including:
    - Missing spaces around operators
    - Missing semicolons
    - Compact object and array formatting

    Args:
        tmp_path: Pytest fixture providing a temporary directory.

    Returns:
        Path to the created file as a string.
    """
    file_path = tmp_path / "unformatted.js"
    file_path.write_text(
        """\
function foo(x,y,z){return x+y+z}
const obj={a:1,b:2,c:3}
const arr=[1,2,3,4,5]
if(true){console.log("hello")}
""",
    )
    return str(file_path)


@pytest.fixture
def temp_js_file_formatted(tmp_path: Path) -> str:
    """Create a temporary JavaScript file that is already formatted.

    Creates a file containing properly formatted JavaScript code that
    Prettier should leave unchanged.

    Args:
        tmp_path: Pytest fixture providing a temporary directory.

    Returns:
        Path to the created file as a string.
    """
    file_path = tmp_path / "formatted.js"
    file_path.write_text(
        """\
function foo(x, y, z) {
  return x + y + z;
}
const obj = { a: 1, b: 2, c: 3 };
const arr = [1, 2, 3, 4, 5];
if (true) {
  console.log("hello");
}
""",
    )
    return str(file_path)


@pytest.fixture
def temp_json_file_unformatted(tmp_path: Path) -> str:
    """Create a temporary JSON file with formatting issues.

    Creates a file containing a single-line JSON object that Prettier
    should format with proper indentation and newlines.

    Args:
        tmp_path: Pytest fixture providing a temporary directory.

    Returns:
        Path to the created file as a string.
    """
    file_path = tmp_path / "unformatted.json"
    file_path.write_text('{"name":"test","version":"1.0.0","dependencies":{}}')
    return str(file_path)


# --- Tests for PrettierPlugin definition ---


@pytest.mark.parametrize(
    ("attr", "expected"),
    [
        ("name", "prettier"),
        ("can_fix", True),
    ],
    ids=["name", "can_fix"],
)
def test_definition_attributes(
    get_plugin: Callable[[str], BaseToolPlugin],
    attr: str,
    expected: object,
) -> None:
    """Verify PrettierPlugin definition has correct attribute values.

    Tests that the plugin definition exposes the expected values for
    name and can_fix attributes.

    Args:
        get_plugin: Fixture factory to get plugin instances.
        attr: The attribute name to check on the definition.
        expected: The expected value of the attribute.
    """
    prettier_plugin = get_plugin("prettier")
    assert_that(getattr(prettier_plugin.definition, attr)).is_equal_to(expected)


def test_definition_file_patterns(get_plugin: Callable[[str], BaseToolPlugin]) -> None:
    """Verify PrettierPlugin definition includes JavaScript/TypeScript file patterns.

    Tests that the plugin is configured to handle JavaScript or TypeScript files.

    Args:
        get_plugin: Fixture factory to get plugin instances.
    """
    prettier_plugin = get_plugin("prettier")
    patterns = prettier_plugin.definition.file_patterns
    has_js_or_ts = "*.js" in patterns or "*.ts" in patterns
    assert_that(has_js_or_ts).is_true()


# --- Integration tests for prettier check command ---


@pytest.mark.parametrize(
    ("file_fixture", "expect_issues"),
    [
        ("temp_js_file_unformatted", True),
        ("temp_js_file_formatted", False),
    ],
    ids=["unformatted_js", "formatted_js"],
)
def test_check_js_file_formatting_state(
    get_plugin: Callable[[str], BaseToolPlugin],
    file_fixture: str,
    expect_issues: bool,
    request: pytest.FixtureRequest,
) -> None:
    """Verify Prettier check correctly detects JS file formatting state.

    Runs Prettier in check mode on files with different formatting states
    and verifies the expected issue count.

    Args:
        get_plugin: Fixture factory to get plugin instances.
        file_fixture: Name of the fixture providing the file path.
        expect_issues: Whether issues are expected (True for unformatted).
        request: Pytest request fixture for dynamic fixture access.
    """
    file_path = request.getfixturevalue(file_fixture)
    prettier_plugin = get_plugin("prettier")
    result = prettier_plugin.check([file_path], {})

    assert_that(result).is_not_none()
    assert_that(result.name).is_equal_to("prettier")
    if expect_issues:
        assert_that(result.issues_count).is_greater_than(0)
    else:
        assert_that(result.issues_count).is_equal_to(0)


def test_check_json_file(
    get_plugin: Callable[[str], BaseToolPlugin],
    temp_json_file_unformatted: str,
) -> None:
    """Verify Prettier check handles JSON files.

    Runs Prettier on a JSON file and verifies a result is returned
    without errors.

    Args:
        get_plugin: Fixture factory to get plugin instances.
        temp_json_file_unformatted: Path to JSON file with formatting issues.
    """
    prettier_plugin = get_plugin("prettier")
    result = prettier_plugin.check([temp_json_file_unformatted], {})

    assert_that(result).is_not_none()


# --- Integration tests for prettier fix command ---


def test_fix_formats_js_file(
    get_plugin: Callable[[str], BaseToolPlugin],
    temp_js_file_unformatted: str,
) -> None:
    """Verify Prettier fix reformats unformatted JS files.

    Runs Prettier fix on a file with formatting issues and verifies
    the file content changes.

    Args:
        get_plugin: Fixture factory to get plugin instances.
        temp_js_file_unformatted: Path to file with formatting issues.
    """
    prettier_plugin = get_plugin("prettier")
    original = Path(temp_js_file_unformatted).read_text()

    result = prettier_plugin.fix([temp_js_file_unformatted], {})

    assert_that(result).is_not_none()
    assert_that(result.name).is_equal_to("prettier")
    assert_that(result.success).is_true()

    new_content = Path(temp_js_file_unformatted).read_text()
    assert_that(new_content).is_not_equal_to(original)


def test_fix_formats_json_file(
    get_plugin: Callable[[str], BaseToolPlugin],
    temp_json_file_unformatted: str,
) -> None:
    """Verify Prettier fix reformats JSON files.

    Runs Prettier fix on a JSON file and verifies the file is
    formatted with proper indentation.

    Args:
        get_plugin: Fixture factory to get plugin instances.
        temp_json_file_unformatted: Path to JSON file with formatting issues.
    """
    prettier_plugin = get_plugin("prettier")
    original = Path(temp_json_file_unformatted).read_text()

    result = prettier_plugin.fix([temp_json_file_unformatted], {})

    assert_that(result).is_not_none()
    assert_that(result.success).is_true()

    new_content = Path(temp_json_file_unformatted).read_text()
    assert_that(new_content).is_not_equal_to(original)


# --- Tests for PrettierPlugin.set_options method ---


@pytest.mark.parametrize(
    ("option_name", "option_value", "expected"),
    [
        ("tab_width", 4, 4),
        ("use_tabs", True, True),
        ("single_quote", True, True),
    ],
    ids=["tab_width", "use_tabs", "single_quote"],
)
def test_set_options(
    get_plugin: Callable[[str], BaseToolPlugin],
    option_name: str,
    option_value: object,
    expected: object,
) -> None:
    """Verify PrettierPlugin.set_options correctly sets various options.

    Tests that plugin options can be set and retrieved correctly.

    Args:
        get_plugin: Fixture factory to get plugin instances.
        option_name: Name of the option to set.
        option_value: Value to set for the option.
        expected: Expected value when retrieving the option.
    """
    prettier_plugin = get_plugin("prettier")
    prettier_plugin.set_options(**{option_name: option_value})
    assert_that(prettier_plugin.options.get(option_name)).is_equal_to(expected)
