#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------
# Copyright (c) 2026 Huawei Technologies Co., Ltd.
# This file is part of the MindStudio project.
#
# MindStudio is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
#    http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# -------------------------------------------------------------------------

from __future__ import annotations

from pathlib import Path

import pytest

from msagent.cli.resolvers.file import FileResolver


@pytest.mark.asyncio
async def test_get_files_falls_back_to_fd_when_git_lookup_fails(
    monkeypatch,
    tmp_path: Path,
) -> None:
    calls: list[list[str]] = []

    async def fake_execute_bash_command(cmd, cwd, timeout):
        del cwd, timeout
        calls.append(cmd)
        if len(calls) == 1:
            return 1, "", "git failed"
        return 0, "src/main.py\nREADME.md\n", ""

    monkeypatch.setattr(
        "msagent.cli.resolvers.file.execute_bash_command",
        fake_execute_bash_command,
    )

    files = await FileResolver._get_files(tmp_path, limit=5, pattern="src")

    assert files == ["src/main.py", "README.md"]
    assert len(calls) == 2
    assert "git ls-files" in calls[0][2]
    assert "fd --type f" in calls[1][2]


@pytest.mark.asyncio
async def test_file_resolver_complete_sorts_dirs_before_files_and_applies_styles(
    monkeypatch,
    tmp_path: Path,
) -> None:
    async def fake_get_files(working_dir, limit=None, pattern=""):
        del working_dir, limit, pattern
        return ["src/main.py", "docs/guide.md"]

    async def fake_get_directories(working_dir, limit=None, pattern=""):
        del working_dir, limit, pattern
        return ["src", "docs"]

    monkeypatch.setattr(FileResolver, "_get_files", staticmethod(fake_get_files))
    monkeypatch.setattr(
        FileResolver,
        "_get_directories",
        staticmethod(fake_get_directories),
    )

    resolver = FileResolver()
    completions = await resolver.complete(
        fragment="s",
        ctx={"working_dir": tmp_path, "start_position": -2},
        limit=10,
    )

    assert [completion.text for completion in completions] == [
        "@:file:docs",
        "@:file:src",
        "@:file:docs/guide.md",
        "@:file:src/main.py",
    ]
    assert [completion.display_text for completion in completions[:2]] == [
        "@:file:docs/",
        "@:file:src/",
    ]
    assert [completion.style for completion in completions] == [
        "class:dir-completion",
        "class:dir-completion",
        "class:file-completion",
        "class:file-completion",
    ]
    assert all(completion.start_position == -2 for completion in completions)


@pytest.mark.asyncio
async def test_file_resolver_complete_returns_empty_when_lookup_raises(
    monkeypatch,
    tmp_path: Path,
) -> None:
    async def failing_get_files(working_dir, limit=None, pattern=""):
        del working_dir, limit, pattern
        raise RuntimeError("boom")

    monkeypatch.setattr(FileResolver, "_get_files", staticmethod(failing_get_files))

    resolver = FileResolver()
    completions = await resolver.complete(
        fragment="x",
        ctx={"working_dir": tmp_path},
        limit=5,
    )

    assert completions == []


def test_file_resolver_resolve_returns_absolute_path_for_valid_reference(
    tmp_path: Path,
) -> None:
    (tmp_path / "src").mkdir()
    file_path = tmp_path / "src" / "main.py"
    file_path.write_text("print('ok')", encoding="utf-8")

    resolver = FileResolver()
    resolved = Path(resolver.resolve("src/main.py", ctx={"working_dir": tmp_path}))

    assert resolved == file_path.resolve()


def test_file_resolver_resolve_falls_back_to_original_on_error(
    monkeypatch,
    tmp_path: Path,
) -> None:
    def broken_resolve_path(*args, **kwargs):
        del args, kwargs
        raise ValueError("invalid")

    monkeypatch.setattr("msagent.cli.resolvers.file.resolve_path", broken_resolve_path)

    resolver = FileResolver()
    result = resolver.resolve("relative/path.txt", ctx={"working_dir": tmp_path})

    assert result == "relative/path.txt"


def test_file_resolver_detects_standalone_absolute_path(tmp_path: Path) -> None:
    file_path = tmp_path / "notes.txt"
    file_path.write_text("note", encoding="utf-8")

    resolver = FileResolver()

    assert resolver.is_standalone_reference(str(file_path)) is True
    assert resolver.is_standalone_reference("notes.txt") is False