# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause


import os
import uuid

import errno
import pytest
from errno import ENOENT, EACCES, EROFS, EPERM
from shutil import rmtree
from contextlib import contextmanager
from tempfile import gettempdir
from os.path import join, isfile, lexists
from stat import S_IRUSR, S_IRGRP, S_IROTH
from stat import S_IRWXG, S_IRWXO, S_IRWXU
from stat import S_IXUSR, S_IXGRP, S_IXOTH
from unittest.mock import patch

from conda.gateways.disk.update import touch


def create_temp_location():
    tempdirdir = gettempdir()
    dirname = str(uuid.uuid4())[:8]
    return join(tempdirdir, dirname)


@contextmanager
def tempdir():
    prefix = create_temp_location()
    try:
        os.makedirs(prefix)
        yield prefix
    finally:
        if lexists(prefix):
            rmtree(prefix, ignore_errors=False, onerror=_remove_read_only)


def _remove_read_only(func, path, exc):
    excvalue = exc[1]
    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
        os.chmod(path, S_IRWXU| S_IRWXG| S_IRWXO)
        func(path)
    else:
        pass


def _make_read_only(path):
    os.chmod(path, S_IRUSR | S_IRGRP | S_IROTH)


def _can_write_file(test, content):
    try:
        with open(test, 'w+') as fh:
            fh.write(content)
            fh.close()
        if os.stat(test).st_size == 0.0:
            return False
        else:
            return True
    except Exception as e:
        eno = getattr(e, 'errono', None)
        if eno == 13:
            return False


def _try_open(path):
    try:
        f = open(path, 'a+')
    except:
        raise
    else:
        f.close()


def _can_execute(path):
    return bool(os.stat(path).st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))


def test_make_writable():
    from conda.gateways.disk.permissions import make_writable
    with tempdir() as td:
        test_path = join(td, 'test_path')
        touch(test_path)
        assert isfile(test_path)
        _try_open(test_path)
        _make_read_only(test_path)
        pytest.raises((IOError, OSError), _try_open, test_path)
        make_writable(test_path)
        _try_open(test_path)
        assert _can_write_file(test_path, "welcome to the ministry of silly walks")
        os.remove(test_path)
        assert not isfile(test_path)


def test_make_writable_doesnt_exist():
    from conda.gateways.disk.permissions import make_writable
    with pytest.raises((IOError, OSError)) as exc:
        make_writable(join('some', 'path', 'that', 'definitely', 'doesnt', 'exist'))
    assert exc.value.errno == ENOENT


def test_make_writable_dir_EPERM():
    import conda.gateways.disk.permissions
    from conda.gateways.disk.permissions import make_writable
    with patch.object(conda.gateways.disk.permissions, 'chmod') as chmod_mock:
        chmod_mock.side_effect = IOError(EPERM, 'some message', 'foo')
        with tempdir() as td:
            assert not make_writable(td)


def test_make_writable_dir_EACCES():
    import conda.gateways.disk.permissions
    from conda.gateways.disk.permissions import make_writable
    with patch.object(conda.gateways.disk.permissions, 'chmod') as chmod_mock:
        chmod_mock.side_effect = IOError(EACCES, 'some message', 'foo')
        with tempdir() as td:
            assert not make_writable(td)


def test_make_writable_dir_EROFS():
    import conda.gateways.disk.permissions
    from conda.gateways.disk.permissions import make_writable
    with patch.object(conda.gateways.disk.permissions, 'chmod') as chmod_mock:
        chmod_mock.side_effect = IOError(EROFS, 'some message', 'foo')
        with tempdir() as td:
            assert not make_writable(td)


def test_recursive_make_writable():
    from conda.gateways.disk.permissions import recursive_make_writable
    with tempdir() as td:
        test_path = join(td, 'test_path')
        touch(test_path)
        assert isfile(test_path)
        _try_open(test_path)
        _make_read_only(test_path)
        pytest.raises((IOError, OSError), _try_open, test_path)
        recursive_make_writable(test_path)
        _try_open(test_path)
        assert _can_write_file(test_path, "welcome to the ministry of silly walks")
        os.remove(test_path)
        assert not isfile(test_path)


def test_make_executable():
    from conda.gateways.disk.permissions import make_executable
    with tempdir() as td:
        test_path = join(td, 'test_path')
        touch(test_path)
        assert isfile(test_path)
        _try_open(test_path)
        _make_read_only(test_path)
        assert not _can_write_file(test_path, "welcome to the ministry of silly walks")
        assert not _can_execute(test_path)
        make_executable(test_path)
