import os.path
import sys

from npe2 import DynamicPlugin

from finn.plugins.utils import (
    MatchFlag,
    get_all_readers,
    get_filename_patterns_for_reader,
    get_potential_readers,
    get_preferred_reader,
    score_specificity,
)
from finn.settings import get_settings


def test_get_preferred_reader_no_readers():
    get_settings().plugins.extension2reader = {}
    reader = get_preferred_reader("my_file.tif")
    assert reader is None


def test_get_preferred_reader_for_extension():
    get_settings().plugins.extension2reader = {"*.tif": "fake-plugin"}
    reader = get_preferred_reader("my_file.tif")
    assert reader == "fake-plugin"


def test_get_preferred_reader_complex_pattern():
    get_settings().plugins.extension2reader = {
        "*/my-specific-folder/*.tif": "fake-plugin"
    }
    reader = get_preferred_reader("/asdf/my-specific-folder/my_file.tif")
    assert reader == "fake-plugin"

    reader = get_preferred_reader("/asdf/foo/my-specific-folder/my_file.tif")
    assert reader == "fake-plugin"


def test_get_preferred_reader_match_less_ambiguous():
    get_settings().plugins.extension2reader = {
        # generic star so least specificity
        "*.tif": "generic-tif-plugin",
        # specific file so most specificity
        "*/foo.tif": "very-specific-plugin",
        # set so less specificity
        "*/file_[0-9][0-9].tif": "set-plugin",
    }

    reader = get_preferred_reader("/asdf/a.tif")
    assert reader == "generic-tif-plugin"

    reader = get_preferred_reader("/asdf/foo.tif")
    assert reader == "very-specific-plugin"

    reader = get_preferred_reader("/asdf/file_01.tif")
    assert reader == "set-plugin"


def test_get_preferred_reader_more_nested():
    get_settings().plugins.extension2reader = {
        # less nested so less specificity
        "*.tif": "generic-tif-plugin",
        # more nested so higher specificity
        "*/my-specific-folder/*.tif": "fake-plugin",
        # even more nested so even higher specificity
        "*/my-specific-folder/nested/*.tif": "very-specific-plugin",
    }

    reader = get_preferred_reader("/asdf/nested/1/2/3/my_file.tif")
    assert reader == "generic-tif-plugin"

    reader = get_preferred_reader("/asdf/my-specific-folder/my_file.tif")
    assert reader == "fake-plugin"

    reader = get_preferred_reader("/asdf/my-specific-folder/nested/my_file.tif")
    assert reader == "very-specific-plugin"


def test_get_preferred_reader_abs_path():
    get_settings().plugins.extension2reader = {
        # abs path so highest specificity
        os.path.realpath("/asdf/*.tif"): "most-specific-plugin",
        # less nested so less specificity
        "*.tif": "generic-tif-plugin",
        # more nested so higher specificity
        "*/my-specific-folder/*.tif": "fake-plugin",
        # even more nested so even higher specificity
        "*/my-specific-folder/nested/*.tif": "very-specific-plugin",
    }

    reader = get_preferred_reader("/asdf/my-specific-folder/nested/my_file.tif")
    assert reader == "most-specific-plugin"


def test_score_specificity_simple():
    assert score_specificity("") == (True, 0, [MatchFlag.NONE])
    assert score_specificity("a") == (True, 0, [MatchFlag.NONE])
    assert score_specificity("ab*c") == (True, 0, [MatchFlag.STAR])
    assert score_specificity("a?c") == (True, 0, [MatchFlag.ANY])
    assert score_specificity("a[a-zA-Z]c") == (True, 0, [MatchFlag.SET])
    assert score_specificity("*[a-zA-Z]*a?c") == (
        True,
        0,
        [MatchFlag.STAR | MatchFlag.ANY | MatchFlag.SET],
    )


def test_score_specificity_complex():
    # account for py313 change in https://github.com/python/cpython/pull/113829
    if sys.platform.startswith("win") and sys.version_info >= (3, 13):
        relative_path = r"*\my-specific-folder\[nested]\*?.tif"
        absolute_path = r"\\my-specific-folder\[nested]\*?.tif"
    else:
        relative_path = "*/my-specific-folder/[nested]/*?.tif"
        absolute_path = "/my-specific-folder/[nested]/*?.tif"

    assert score_specificity(relative_path) == (
        True,
        -3,
        [
            MatchFlag.STAR,
            MatchFlag.NONE,
            MatchFlag.SET,
            MatchFlag.STAR | MatchFlag.ANY,
        ],
    )
    assert score_specificity(absolute_path) == (
        False,
        -2,
        [
            MatchFlag.NONE,
            MatchFlag.SET,
            MatchFlag.STAR | MatchFlag.ANY,
        ],
    )


def test_score_specificity_collapse_star():
    assert score_specificity("*/*/?*.tif") == (
        True,
        -1,
        [MatchFlag.STAR, MatchFlag.STAR | MatchFlag.ANY],
    )
    assert score_specificity("*/*/*a?c.tif") == (
        True,
        0,
        [MatchFlag.STAR | MatchFlag.ANY],
    )
    assert score_specificity("*/*/*.tif") == (True, 0, [MatchFlag.STAR])
    assert score_specificity("*/abc*/*.tif") == (
        True,
        -1,
        [MatchFlag.STAR, MatchFlag.STAR],
    )
    # account for py313 change in https://github.com/python/cpython/pull/113829
    if sys.platform.startswith("win") and sys.version_info >= (3, 13):
        absolute_path = r"\\abc*\*.tif"
    else:
        absolute_path = "/abc*/*.tif"

    assert score_specificity(absolute_path) == (False, 0, [MatchFlag.STAR])


def test_score_specificity_range():
    _, _, score = score_specificity("[abc")
    assert score == [MatchFlag.NONE]

    _, _, score = score_specificity("[abc]")
    assert score == [MatchFlag.SET]

    _, _, score = score_specificity("[abc[")
    assert score == [MatchFlag.NONE]

    _, _, score = score_specificity("][abc")
    assert score == [MatchFlag.NONE]

    _, _, score = score_specificity("[[abc]]")
    assert score == [MatchFlag.SET]


def test_get_preferred_reader_no_extension():
    assert get_preferred_reader("my_file") is None


def test_get_preferred_reader_full_path(tmp_path, monkeypatch):
    (tmp_path / "my_file.zarr").mkdir()
    zarr_path = str(tmp_path / "my_file.zarr")

    assert get_preferred_reader(zarr_path) is None
    get_settings().plugins.extension2reader[f"{zarr_path}/"] = "fake-plugin"
    assert get_preferred_reader(zarr_path) == "fake-plugin"
    monkeypatch.chdir(tmp_path)
    assert get_preferred_reader("./my_file.zarr") == "fake-plugin"


def test_get_potential_readers_finds_readers(tmp_plugin: DynamicPlugin):
    tmp2 = tmp_plugin.spawn(register=True)

    @tmp_plugin.contribute.reader(filename_patterns=["*.tif"])
    def read_tif(path): ...

    @tmp2.contribute.reader(filename_patterns=["*.*"])
    def read_all(path): ...

    readers = get_potential_readers("my_file.tif")
    assert len(readers) == 2


def test_get_potential_readers_extension_case(tmp_plugin: DynamicPlugin):
    @tmp_plugin.contribute.reader(filename_patterns=["*.tif"])
    def read_tif(path): ...

    readers = get_potential_readers("my_file.TIF")
    assert len(readers) == 1


def test_get_potential_readers_none_available():
    assert not get_potential_readers("my_file.fake")


def test_get_potential_readers_plugin_name_disp_name(
    tmp_plugin: DynamicPlugin,
):
    @tmp_plugin.contribute.reader(filename_patterns=["*.fake"])
    def read_tif(path): ...

    readers = get_potential_readers("my_file.fake")
    assert readers[tmp_plugin.name] == tmp_plugin.display_name


def test_get_all_readers(tmp_plugin: DynamicPlugin):
    tmp2 = tmp_plugin.spawn(register=True)

    @tmp_plugin.contribute.reader(filename_patterns=["*.fake"])
    def read_tif(path): ...

    @tmp2.contribute.reader(filename_patterns=[".fake2"])
    def read_all(path): ...

    npe2_readers, npe1_readers = get_all_readers()
    assert len(npe2_readers) == 2
    assert len(npe1_readers) == 0


def test_get_filename_patterns_fake_plugin():
    assert len(get_filename_patterns_for_reader("gibberish")) == 0


def test_get_filename_patterns(tmp_plugin: DynamicPlugin):
    @tmp_plugin.contribute.reader(filename_patterns=["*.tif"])
    def read_tif(path): ...

    @tmp_plugin.contribute.reader(filename_patterns=["*.csv"])
    def read_csv(pth): ...

    patterns = get_filename_patterns_for_reader(tmp_plugin.name)
    assert len(patterns) == 2
    assert "*.tif" in patterns
    assert "*.csv" in patterns
