"""Unittest module for media downloader."""
import os
import sys
import unittest
from dataclasses import asdict
from unittest.mock import patch

from utils.format import (
    Link,
    create_progress_bar,
    extract_info_from_link,
    format_byte,
    get_byte_from_str,
    replace_date_time,
    truncate_filename,
    validate_title,
)

sys.path.append("..")  # Adds higher directory to python modules path.


class FormatTestCase(unittest.TestCase):
    def test_format_byte(self):
        byte_list = [
            "KB",
            "MB",
            "GB",
            "TB",
            "PB",
            "EB",
            "ZB",
            "YB",
            "BB",
            "NB",
            "DB",
            "CB",
        ]

        self.assertEqual(format_byte(0.1), "0.8b")
        self.assertEqual(format_byte(1), "1B")

        for i, value in enumerate(byte_list):
            self.assertEqual(format_byte(pow(1024, i + 1)), "1.0" + value)

        try:
            format_byte(-1)
        except Exception as e:
            self.assertEqual(isinstance(e, ValueError), True)

    def test_replace_date_time(self):
        self.assertEqual(
            replace_date_time(""),
            "",
        )

        # split by '.'
        self.assertEqual(
            replace_date_time("xxxxx 2020.03.08 xxxxxxxxx"),
            "xxxxx 2020-03-08 00:00:00 xxxxxxxxx",
        )

        # split by '-'
        self.assertEqual(
            replace_date_time("xxxxx 2020-03-08 xxxxxxxxxxxx"),
            "xxxxx 2020-03-08 00:00:00 xxxxxxxxxxxx",
        )

        # split by '/'
        self.assertEqual(
            replace_date_time("xasd as 2020/03/08 21321fszv"),
            "xasd as 2020-03-08 00:00:00 21321fszv",
        )

        # more different date
        self.assertEqual(
            replace_date_time("xxxxx 2020.03.08 2020.03.09 14:51 xxxxxxxxx"),
            "xxxxx 2020-03-08 00:00:00 2020-03-09 14:51:00 xxxxxxxxx",
        )

        # more space
        self.assertEqual(
            replace_date_time("xxxxx 2020.03.08 2020.03.09      14:51 xxxxxxxxx"),
            "xxxxx 2020-03-08 00:00:00 2020-03-09 14:51:00 xxxxxxxxx",
        )

        # more date format
        self.assertEqual(
            replace_date_time("xasd as 2020/03 21321fszv"),
            "xasd as 2020-03-01 00:00:00 21321fszv",
        )
        self.assertEqual(
            replace_date_time("xasd as 2020-03 21321fszv"),
            "xasd as 2020-03-01 00:00:00 21321fszv",
        )
        self.assertEqual(
            replace_date_time("xasd as 2020.03 21321fszv"),
            "xasd as 2020-03-01 00:00:00 21321fszv",
        )

    def test_get_byte_from_str(self):
        # B
        self.assertEqual(get_byte_from_str("2B"), 2)
        # KB
        self.assertEqual(get_byte_from_str("2KB"), 2 * 1024)
        self.assertEqual(get_byte_from_str("1024KB"), 1024 * 1024)
        self.assertEqual(get_byte_from_str("2024KB"), 2024 * 1024)
        self.assertEqual(get_byte_from_str("4000KB"), 4000 * 1024)

        # MB
        self.assertEqual(get_byte_from_str("2MB"), 2 * 1024 * 1024)
        self.assertEqual(get_byte_from_str("1024MB"), 1024 * 1024 * 1024)

        # GB
        self.assertEqual(get_byte_from_str("2GB"), 2 * 1024 * 1024 * 1024)

        # TB
        self.assertEqual(get_byte_from_str("2TB"), 2 * 1024 * 1024 * 1024 * 1024)
        self.assertEqual(get_byte_from_str("1024TB"), 1024 * 1024 * 1024 * 1024 * 1024)

        # more str
        self.assertEqual(get_byte_from_str("2BW"), 2)
        self.assertEqual(get_byte_from_str("2WBW"), None)

        self.assertEqual(get_byte_from_str("2CB"), None)

    def test_extract_info_from_link(self):
        test_cases = [
            ("https://t.me/", Link(group_id=None)),
            ("https://t.me/username/1234", Link(group_id="username", post_id=1234)),
            ("https://t.me/username", Link(group_id="username")),
            ("https://t.me/c/213213/91011", Link(group_id=-100213213, post_id=91011)),
            (
                "https://t.me/test123/1/1234",
                Link(group_id="test123", topic_id=1, post_id=1234),
            ),
            ("me", Link(group_id="me")),
            ("self", Link(group_id="self")),
            (
                "https://t.me/opencfdchannel/4434?comment=360409",
                Link(group_id="opencfdchannel", comment_id=360409),
            ),
            ("https://t.me/telegram/193", Link(group_id="telegram", post_id=193)),
            (
                "https://t.me/c/1697797156/151",
                Link(group_id=-1001697797156, post_id=151),
            ),
            (
                "https://t.me/iFreeKnow/45662/55005",
                Link(group_id="iFreeKnow", topic_id=45662, post_id=55005),
            ),
            (
                "https://t.me/c/1492447836/251015/251021",
                Link(group_id=-1001492447836, topic_id=251015, post_id=251021),
            ),
        ]

        for link, expected in test_cases:
            result = extract_info_from_link(link)
            self.assertEqual(asdict(result), asdict(expected))

    def test_create_progress_bar(self):
        progress = 50
        progress_bar = create_progress_bar(progress)
        self.assertEqual(progress_bar, "█████░░░░░")

    def test_create_progress_bar_with_custom_bars(self):
        progress = 75
        total_bars = 20
        progress_bar = create_progress_bar(progress, total_bars)
        self.assertEqual(progress_bar, "███████████████░░░░░")


class TestTruncateFilename(unittest.TestCase):
    def test_truncate_filename(self):
        test_cases = [
            ("testfile.txt", 240, "testfile.txt"),
            ("testfile.txt", 5, "t.txt"),
            ("a" * 240 + ".txt", 240, "a" * 236 + ".txt"),
            ("a" * 241 + ".txt", 240, "a" * 236 + ".txt"),
        ]

        for path, limit, expected in test_cases:
            self.assertEqual(truncate_filename(path, limit), expected)

    @unittest.skipIf(sys.platform.startswith("win"), "requires Unix-based system")
    def test_linux_filename_too_long(self):
        long_filename = "a" * 265 + ".txt"
        with self.assertRaises(OSError):
            with open(long_filename, "w") as f:
                f.write("test")

        long_filename = "a" * 265 + ".txt"
        long_filename = truncate_filename(long_filename) + ".temp"
        try:
            with open(long_filename, "w") as f:
                f.write("test")
            os.remove(long_filename)
        except Exception:
            self.assertEqual(False, True)

    @unittest.skipIf(not sys.platform.startswith("win"), "requires Windows system")
    def test_windows_filename_too_long(self):
        long_filename = "a" * 265 + ".txt"
        with self.assertRaises(OSError):
            with open(long_filename, "w") as f:
                f.write("test")

        long_filename = "a" * 265 + ".txt"
        long_filename = truncate_filename(long_filename) + ".temp"
        try:
            with open(long_filename, "w") as f:
                f.write("test")
            os.remove(long_filename)
        except Exception:
            self.assertEqual(False, True)

    @patch("builtins.open", unittest.mock.mock_open())
    def test_file_creation(self):
        file_name = "a" * 240 + ".txt"
        truncated_file_name = truncate_filename(file_name)

        with open(truncated_file_name, "w") as f:
            f.write("test")

        open.assert_called_once_with(truncated_file_name, "w")


class TestValidateTitle(unittest.TestCase):
    def test_validate_title(self):
        test_cases = [
            ("Hello, World!", "Hello, World!"),
            ("Invalid/Title", "Invalid_Title"),
            ("File\\Name", "File_Name"),
            ("Colons:Are:Not:Allowed", "Colons_Are_Not_Allowed"),
            ("Asterisks*In*Title", "Asterisks_In_Title"),
            ("Question?Mark", "Question_Mark"),
            ('Double"Quotes', "Double_Quotes"),
            ("Less<Than", "Less_Than"),
            ("Greater>Than", "Greater_Than"),
            ("Pipe|Symbol", "Pipe_Symbol"),
            ("Multi\nLine", "Multi_Line"),
        ]

        for title, expected in test_cases:
            with self.subTest(title=title, expected=expected):
                self.assertEqual(validate_title(title), expected)

    @patch("utils.format.re.sub")
    def test_mock_re_sub(self, mock_re_sub):
        title = "Invalid/Title"
        mock_re_sub.return_value = "Mocked_Title"

        result = validate_title(title)
        self.assertEqual(result, "Mocked_Title")
        mock_re_sub.assert_called_once_with(r"[/\\:*?\"<>|\n]", "_", title)
