import collections.abc
import os.path

import pytest

import fsspec
from fsspec.implementations.tests.test_archive import archive_data, tempzip


def test_info():
    with tempzip(archive_data) as z:
        fs = fsspec.filesystem("zip", fo=z)

        # Iterate over all files.
        for f in archive_data:
            lhs = fs.info(f)

            # Probe some specific fields of Zip archives.
            assert "CRC" in lhs
            assert "compress_size" in lhs


def test_fsspec_get_mapper():
    """Added for #788"""

    with tempzip(archive_data) as z:
        mapping = fsspec.get_mapper(f"zip::{z}")

        assert isinstance(mapping, collections.abc.Mapping)
        keys = sorted(mapping.keys())
        assert keys == ["a", "b", "deeply/nested/path"]

        # mapping.getitems() will call FSMap.fs.cat()
        # which was not accurately implemented for zip.
        assert isinstance(mapping, fsspec.mapping.FSMap)
        items = dict(mapping.getitems(keys))
        assert items == {"a": b"", "b": b"hello", "deeply/nested/path": b"stuff"}


def test_not_cached():
    with tempzip(archive_data) as z:
        fs = fsspec.filesystem("zip", fo=z)
        fs2 = fsspec.filesystem("zip", fo=z)
        assert fs is not fs2


def test_root_info():
    with tempzip(archive_data) as z:
        fs = fsspec.filesystem("zip", fo=z)
        assert fs.info("/") == {"name": "", "type": "directory", "size": 0}
        assert fs.info("") == {"name": "", "type": "directory", "size": 0}


def test_write_seek(m):
    with m.open("afile.zip", "wb") as f:
        fs = fsspec.filesystem("zip", fo=f, mode="w")
        fs.pipe("another", b"hi")
        fs.zip.close()

    with m.open("afile.zip", "rb") as f:
        fs = fsspec.filesystem("zip", fo=f)
        assert fs.cat("another") == b"hi"


def test_rw(m):
    # extra arg to zip means "create archive"
    with fsspec.open(
        "zip://afile::memory://out.zip", mode="wb", zip={"mode": "w"}
    ) as f:
        f.write(b"data")

    with fsspec.open("zip://afile::memory://out.zip", mode="rb") as f:
        assert f.read() == b"data"


def test_mapper(m):
    # extra arg to zip means "create archive"
    mapper = fsspec.get_mapper("zip::memory://out.zip", zip={"mode": "w"})
    with pytest.raises(KeyError):
        mapper["a"]

    mapper["a"] = b"data"
    with pytest.raises(OSError):
        # fails because this is write mode and we cannot also read
        mapper["a"]
    assert "a" in mapper  # but be can list


def test_zip_glob_star(m):
    with fsspec.open(
        "zip://adir/afile::memory://out.zip", mode="wb", zip={"mode": "w"}
    ) as f:
        f.write(b"data")

    fs, _ = fsspec.core.url_to_fs("zip::memory://out.zip")
    outfiles = fs.glob("*")
    assert len(outfiles) == 1

    fs = fsspec.filesystem("zip", fo="memory://out.zip", mode="w")
    fs.mkdir("adir")
    fs.pipe("adir/afile", b"data")
    outfiles = fs.glob("*")
    assert len(outfiles) == 1

    fn = f"{os.path.dirname(os.path.abspath((__file__)))}/out.zip"
    fs = fsspec.filesystem("zip", fo=fn, mode="r")
    outfiles = fs.glob("*")
    assert len(outfiles) == 1


def test_append(m, tmpdir):
    fs = fsspec.filesystem("zip", fo="memory://out.zip", mode="w")
    with fs.open("afile", "wb") as f:
        f.write(b"data")
    fs.close()

    fs = fsspec.filesystem("zip", fo="memory://out.zip", mode="a")
    with fs.open("bfile", "wb") as f:
        f.write(b"data")
    fs.close()

    assert len(fsspec.open_files("zip://*::memory://out.zip")) == 2

    fs = fsspec.filesystem("zip", fo=f"{tmpdir}/out.zip", mode="w")
    with fs.open("afile", "wb") as f:
        f.write(b"data")
    fs.close()

    fs = fsspec.filesystem("zip", fo=f"{tmpdir}/out.zip", mode="a")
    with fs.open("bfile", "wb") as f:
        f.write(b"data")
    fs.close()

    assert len(fsspec.open_files("zip://*::memory://out.zip")) == 2
