# Copyright 2015-2025 Flavio Garcia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
File system functions tests
"""

import tests
from cartola import fs
import os
import unittest


class FsExtensionTestCase(unittest.TestCase):

    def test_extension_file_with_extension(self):
        """ Happy scenario, a filename with an extension
        """
        filename = 'the_file.ext'
        expected_extension = 'ext'
        extension = fs.get_file_extension(filename)
        self.assertEqual(extension, expected_extension)
        self.assertTrue(fs.file_has_extension(filename))

    def test_extension_file_ending_with_dot(self):
        """ Filename ending with a dot
        """
        filename = 'the_file.'
        extension = fs.get_file_extension(filename)
        self.assertIsNone(extension)
        self.assertFalse(fs.file_has_extension(filename))

    def test_extension_file_no_extension(self):
        """ Filename without extension
        """
        filename = 'the_file'
        extension = fs.get_file_extension(filename)
        self.assertIsNone(extension)
        self.assertFalse(fs.file_has_extension(filename))


class FsModuleTestCase(unittest.TestCase):

    def tearDown(self):
        fs.remove_existing(tests.get_sandbox_path(
            os.path.join("monster", "__init__.py")))
        fs.remove_existing(tests.get_sandbox_path(
            os.path.join("monster", "of", "__init__.py")))
        fs.remove_existing(tests.get_sandbox_path(
            os.path.join("monster", "of", "the", "__init__.py")))
        fs.remove_existing(tests.get_sandbox_path(
            os.path.join("monster", "of", "the", "lake", "__init__.py")))
        fs.rmdir_existing(tests.get_sandbox_path(
            os.path.join("monster", "of", "the", "lake")))
        fs.rmdir_existing(tests.get_sandbox_path(
            os.path.join("monster", "of", "the")))
        fs.rmdir_existing(tests.get_sandbox_path(
            os.path.join("monster", "of")))
        fs.rmdir_existing(tests.get_sandbox_path(
            os.path.join("monster")))

    def test_fs_create_module(self):
        """ Create a module structure in a target directory. """
        fs.create_module("monster.of.the.lake", tests.SANDBOX_PATH)
        self.assertTrue(os.path.exists(tests.get_sandbox_path(
            os.path.join("monster", "of", "the", "lake")
        )))
        self.assertTrue(os.path.exists(tests.get_sandbox_path(
            os.path.join("monster", "__init__.py")
        )))
        self.assertTrue(os.path.exists(tests.get_sandbox_path(
            os.path.join("monster", "of", "__init__.py")
        )))
        self.assertTrue(os.path.exists(tests.get_sandbox_path(
            os.path.join("monster", "of", "the", "__init__.py")
        )))
        self.assertTrue(os.path.exists(tests.get_sandbox_path(
            os.path.join("monster", "of", "the", "lake", "__init__.py")
        )))


class FsBasicIOOperationsTestCase(unittest.TestCase):

    def setUp(self):
        """ Application configuration file will be read and components will be
        loaded.
        """
        self.file_to_read = "cartola_sandbox.txt"
        self.file_to_read_path = tests.get_sandbox_path(self.file_to_read)
        self.file_to_read_offset = "cartola_file_to_read_offset.txt"
        self.file_to_read_path_offset = tests.get_sandbox_path(
                self.file_to_read_offset)
        self.file_to_append = "cartola_file_append_test.txt"
        self.file_to_write = "cartola_file_write_test.txt"
        self.file_to_write_path = tests.get_sandbox_path(self.file_to_append)
        self.file_to_append_path = tests.get_sandbox_path(self.file_to_write)
        fs.remove_existing(self.file_to_write_path)

    def tearDown(self):
        fs.remove_existing(self.file_to_append_path)
        fs.remove_existing(self.file_to_write_path)

    def test_string_fs_read(self):
        """ Read a file returning string as result. """
        expected = "Do not remove this file.\n"
        value = fs.read(self.file_to_read_path)
        self.assertIsInstance(value, str)
        self.assertEqual(expected, value)

    def test_bidnary_fs_read_offset(self):
        """ Read a file returning byte as result. """
        expected = b"line 1\n"
        value = fs.read(self.file_to_read_path_offset, binary=True, offset=1)
        self.assertEqual(expected, value)
        expected = b"line 3\nline 4\nline 5\n"
        value = fs.read(self.file_to_read_path_offset, binary=True,
                        start_line=3, limit=3)
        self.assertEqual(expected, value)
        expected = b"line 10\nline 11\n"
        value = fs.read(self.file_to_read_path_offset, binary=True,
                        start_line=10, limit=2)
        self.assertEqual(expected, value)
        expected = b"line 20\n"
        value = fs.read(self.file_to_read_path_offset, binary=True, offset=-1)
        self.assertEqual(expected, value)

    def test_string_fs_read_offset(self):
        """ Read a file returning string as result. """
        expected = "line 1\n"
        value = fs.read(self.file_to_read_path_offset, offset=1)
        self.assertEqual(expected, value)
        expected = "line 3\nline 4\nline 5\n"
        value = fs.read(self.file_to_read_path_offset, start_line=3, limit=3)
        self.assertEqual(expected, value)
        expected = "line 10\nline 11\n"
        value = fs.read(self.file_to_read_path_offset, start_line=10, limit=2)
        self.assertEqual(expected, value)
        expected = "line 20\n"
        value = fs.read(self.file_to_read_path_offset, offset=-1)
        self.assertEqual(expected, value)

    def test_binary_fs_read(self):
        """ Read a file returning bytes as result. """
        expected = b"Do not remove this file.\n"
        value = fs.read(self.file_to_read_path, True)
        self.assertIsInstance(value, bytes)
        self.assertEqual(expected, value)

    def test_fs_b_append(self):
        """ Append in a file using data as bytes. """
        data = b"My text to append.\n"
        data_appended = b"My text to append.\nMy text to append.\n"
        fs.b_append(self.file_to_append_path, data)
        self.assertTrue(os.path.exists(self.file_to_append_path))
        self.assertEqual(data, fs.read(self.file_to_append_path, True))
        fs.b_append(self.file_to_append_path, data)
        self.assertEqual(data_appended,
                         fs.read(self.file_to_append_path, True))

    def test_fs_s_append(self):
        """ Append in a file using data as string. """
        data = "My text to append.\n"
        data_appended = "My text to append.\nMy text to append.\n"
        fs.s_append(self.file_to_append_path, data)
        self.assertTrue(os.path.exists(self.file_to_append_path))
        self.assertEqual(data, fs.read(self.file_to_append_path))
        fs.s_append(self.file_to_append_path, data)
        self.assertEqual(data_appended, fs.read(self.file_to_append_path))

    def test_fs_s_write(self):
        """ Write in a file using data as string. """
        data = "My text to write.\n"
        fs.s_write(self.file_to_write_path, data)
        self.assertTrue(os.path.exists(self.file_to_write_path))
        self.assertEqual(data, fs.read(self.file_to_write_path))

    def test_fs_b_write(self):
        """ Write in a file using data as bytes. """
        data = b"My text to write.\n"
        fs.b_write(self.file_to_write_path, data)
        self.assertTrue(os.path.exists(self.file_to_write_path))
        self.assertEqual(data, fs.read(self.file_to_write_path, True))

    def test_fs_touch(self):
        """ Create(touch) a new file. """
        file_target = tests.get_sandbox_path("file_to_touch.txt")
        self.assertFalse(os.path.exists(file_target))
        fs.touch(file_target)
        self.assertTrue(os.path.exists(file_target))
        fs.remove_existing(file_target)

    def test_fs_remove_existing(self):
        """ Remove a file is it exists. """
        file_target = tests.get_sandbox_path("remove_existing_file.txt")
        self.assertFalse(fs.remove_existing(file_target))
        fs.touch(file_target)
        self.assertTrue(os.path.exists(file_target))
        self.assertTrue(fs.remove_existing(file_target))
        self.assertFalse(os.path.exists(file_target))

    def test_fs_rmdir_existing(self):
        """ Remove a directory is it exists."""
        dir_target = tests.get_sandbox_path("rmdir_existing_dir")
        self.assertFalse(fs.rmdir_existing(dir_target))
        os.mkdir(dir_target)
        self.assertTrue(os.path.exists(dir_target))
        self.assertTrue(fs.rmdir_existing(dir_target))
        self.assertFalse(os.path.exists(dir_target))

    def test_fs_only_dirs_from(self):
        """ Test only_dirs_from function from fs """
        dir_target = tests.get_sandbox_path("parent_dir")
        for child_dir in fs.only_dirs_from(dir_target, False):
            self.assertTrue(os.path.isdir(os.path.join(dir_target, child_dir)))
        for child_dir in fs.only_dirs_from(dir_target):
            self.assertTrue(os.path.isdir(child_dir))
        result = fs.only_dirs_from(
            os.path.join(dir_target, "child_file_1.txt"))
        self.assertTrue(result is None)

    def test_fs_only_files_from(self):
        """ Test only_files_from function from fs """
        dir_target = tests.get_sandbox_path("parent_dir")
        for child_file in fs.only_files_from(dir_target, False):
            self.assertTrue(os.path.isfile(os.path.join(dir_target,
                                                        child_file)))
        for child_file in fs.only_files_from(dir_target):
            self.assertTrue(os.path.isfile(child_file))
