from __future__ import annotations

import json
import os
from pathlib import Path
import sys
import tempfile
import unittest

from click.testing import CliRunner


SRC_ROOT = Path(__file__).resolve().parents[2] / "src"

if str(SRC_ROOT) not in sys.path:
    sys.path.insert(0, str(SRC_ROOT))


from ffx import cli  # noqa: E402


class RenameCliTests(unittest.TestCase):
    def setUp(self):
        self.tempdir = tempfile.TemporaryDirectory()
        self.workspace = Path(self.tempdir.name)
        self.home_dir = self.workspace / "home"
        self.home_dir.mkdir()

    def tearDown(self):
        self.tempdir.cleanup()

    def write_source(self, filename: str, payload: bytes = b"episode") -> Path:
        source_path = self.workspace / filename
        source_path.write_bytes(payload)
        return source_path

    def write_config(self, data: dict) -> None:
        config_dir = self.home_dir / ".local" / "etc"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "ffx.json").write_text(json.dumps(data), encoding="utf-8")

    def invoke_rename(self, *args: str):
        runner = CliRunner()
        result = runner.invoke(
            cli.ffx,
            ["rename", *args],
            env={**os.environ, "HOME": str(self.home_dir)},
        )
        self.assertEqual(0, result.exit_code, result.output)
        return result

    def test_rename_moves_matching_file_in_place(self):
        source_path = self.write_source("demo_S02E03.mkv", b"season-episode")

        result = self.invoke_rename("--prefix", "dball", str(source_path))

        target_path = self.workspace / "dball_s02e03.mkv"
        self.assertIn("demo_S02E03.mkv -> dball_s02e03.mkv", result.output)
        self.assertFalse(source_path.exists())
        self.assertTrue(target_path.exists())
        self.assertEqual(b"season-episode", target_path.read_bytes())

    def test_rename_uses_default_season_and_suffix_for_episode_only_match(self):
        source_path = self.write_source("demo_E07.mp4", b"episode-only")

        result = self.invoke_rename(
            "--prefix",
            "dball",
            "--suffix",
            "bonus",
            str(source_path),
        )

        target_path = self.workspace / "dball_s01e07_bonus.mp4"
        self.assertIn("demo_E07.mp4 -> dball_s01e07_bonus.mp4", result.output)
        self.assertFalse(source_path.exists())
        self.assertTrue(target_path.exists())
        self.assertEqual(b"episode-only", target_path.read_bytes())

    def test_rename_cli_season_overrides_source_season(self):
        source_path = self.write_source("demo_s02e07.webm")

        result = self.invoke_rename(
            "--prefix",
            "dball",
            "--season",
            "5",
            str(source_path),
        )

        target_path = self.workspace / "dball_s05e07.webm"
        self.assertIn("demo_s02e07.webm -> dball_s05e07.webm", result.output)
        self.assertFalse(source_path.exists())
        self.assertTrue(target_path.exists())

    def test_rename_dry_run_prints_mapping_without_moving(self):
        source_path = self.write_source("demo_E07.mkv")

        result = self.invoke_rename(
            "--dry-run",
            "--prefix",
            "dball",
            str(source_path),
        )

        target_path = self.workspace / "dball_s01e07.mkv"
        self.assertIn("demo_E07.mkv -> dball_s01e07.mkv", result.output)
        self.assertTrue(source_path.exists())
        self.assertFalse(target_path.exists())

    def test_rename_uses_configured_indicator_digit_lengths(self):
        self.write_config(
            {
                "defaultIndicatorSeasonDigits": 3,
                "defaultIndicatorEpisodeDigits": 4,
            }
        )
        source_path = self.write_source("demo_E07.mkv")

        result = self.invoke_rename("--prefix", "dball", str(source_path))

        target_path = self.workspace / "dball_s001e0007.mkv"
        self.assertIn("demo_E07.mkv -> dball_s001e0007.mkv", result.output)
        self.assertFalse(source_path.exists())
        self.assertTrue(target_path.exists())

    def test_rename_skips_non_matching_filenames(self):
        source_path = self.write_source("demo_finale.mkv")

        result = self.invoke_rename("--prefix", "dball", str(source_path))

        self.assertIn("No matching files found.", result.output)
        self.assertTrue(source_path.exists())


if __name__ == "__main__":
    unittest.main()
