import os

import core.imports.album_naming as album_naming
import core.imports.paths as import_paths


class _Config:
    def __init__(self, values):
        self._values = values

    def get(self, key, default=None):
        return self._values.get(key, default)


def test_sanitize_filename_replaces_illegal_characters():
    assert import_paths.sanitize_filename("AC/DC: Song?") == "AC_DC_ Song_"
    assert import_paths.sanitize_filename("AUX.txt").startswith("_")


# ── #767: dry-run path build must not create folders ──────────────────────

def _album_path_config(tmp_path):
    return _Config({
        "soulseek.transfer_path": str(tmp_path / "Transfer"),
        "file_organization.enabled": True,
        "file_organization.templates": {
            "album_path": "$albumartist/$albumartist - $album/$track - $title",
            "single_path": "$artist/$artist - $title",
        },
        "file_organization.collab_artist_mode": "first",
        "file_organization.disc_label": "Disc",
    })


def _album_context():
    return {
        "artist": {"name": "Lenka"},
        "album": {"name": "Lenka", "id": "album-1", "release_date": "2008-01-01",
                  "total_tracks": 12, "album_type": "album", "artists": [{"name": "Lenka"}]},
        "track_info": {"name": "The Show", "id": "t1", "track_number": 1,
                       "disc_number": 1, "artists": [{"name": "Lenka"}]},
        "original_search_result": {"title": "The Show", "clean_title": "The Show",
                                   "clean_album": "Lenka", "clean_artist": "Lenka",
                                   "artists": [{"name": "Lenka"}]},
        "source": "deezer", "is_album_download": False,
    }


def test_create_dirs_false_does_not_create_folders(monkeypatch, tmp_path):
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _album_path_config(tmp_path))
    monkeypatch.setattr(import_paths, "_get_album_tracks_for_source", lambda *a: None)

    final_path, created = import_paths.build_final_path_for_track(
        _album_context(), {"name": "Lenka"},
        {"is_album": True, "album_name": "Lenka", "track_number": 1, "disc_number": 1},
        ".flac", create_dirs=False,
    )

    # Path is still computed correctly...
    assert created is True
    assert final_path == str(tmp_path / "Transfer" / "Lenka" / "Lenka - Lenka" / "01 - The Show.flac")
    # ...but NOTHING was written to disk — not even the Transfer root.
    assert not (tmp_path / "Transfer").exists()


def test_create_dirs_true_still_creates_folders(monkeypatch, tmp_path):
    # The download/import flow must keep working (default behavior unchanged).
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _album_path_config(tmp_path))
    monkeypatch.setattr(import_paths, "_get_album_tracks_for_source", lambda *a: None)

    final_path, created = import_paths.build_final_path_for_track(
        _album_context(), {"name": "Lenka"},
        {"is_album": True, "album_name": "Lenka", "track_number": 1, "disc_number": 1},
        ".flac",  # create_dirs defaults True
    )

    assert created is True
    assert (tmp_path / "Transfer" / "Lenka" / "Lenka - Lenka").is_dir()


def test_create_dirs_false_and_true_yield_identical_path(monkeypatch, tmp_path):
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _album_path_config(tmp_path))
    monkeypatch.setattr(import_paths, "_get_album_tracks_for_source", lambda *a: None)

    dry, _ = import_paths.build_final_path_for_track(
        _album_context(), {"name": "Lenka"},
        {"is_album": True, "album_name": "Lenka", "track_number": 1, "disc_number": 1},
        ".flac", create_dirs=False,
    )
    live, _ = import_paths.build_final_path_for_track(
        _album_context(), {"name": "Lenka"},
        {"is_album": True, "album_name": "Lenka", "track_number": 1, "disc_number": 1},
        ".flac", create_dirs=True,
    )
    assert dry == live


def test_build_simple_download_destination_uses_album_folder(monkeypatch, tmp_path):
    config = _Config({"soulseek.transfer_path": str(tmp_path / "Transfer")})
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)

    destination, album_name, filename = import_paths.build_simple_download_destination(
        {
            "search_result": {
                "filename": "Album Folder/source.flac",
                "album": "Album Folder",
            }
        },
        str(tmp_path / "source.flac"),
    )

    assert destination == tmp_path / "Transfer" / "Album Folder" / "source.flac"
    assert album_name == "Album Folder"
    assert filename == "source.flac"
    assert destination.parent.exists()


def test_build_simple_download_destination_falls_back_to_transfer_root(monkeypatch, tmp_path):
    config = _Config({"soulseek.transfer_path": str(tmp_path / "Transfer")})
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)

    destination, album_name, filename = import_paths.build_simple_download_destination(
        {
            "search_result": {
                "filename": "source.flac",
                "album": "Unknown Album",
            }
        },
        str(tmp_path / "source.flac"),
    )

    assert destination == tmp_path / "Transfer" / "source.flac"
    assert album_name == ""
    assert filename == "source.flac"
    assert destination.parent.exists()


def test_get_file_path_from_template_raw_handles_quality_and_disc_placeholders(monkeypatch):
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _Config({}))

    folder_path, filename = import_paths.get_file_path_from_template_raw(
        "$artist/$album/$discnum - $title [$quality]",
        {
            "artist": "Artist One",
            "album": "Album One",
            "title": "Song One",
            "quality": "FLAC 16bit",
            "disc_number": 3,
        },
    )

    assert folder_path == os.path.join("Artist One", "Album One")
    assert filename == "3 - Song One [FLAC 16bit]"


def test_get_file_path_from_template_raw_substitutes_cdnum_for_multi_disc(monkeypatch):
    """$cdnum should expand to 'CDxx' on multi-disc albums (regression).

    Reported by user: filenames had literal '$cdnum' instead of 'CD02'
    because `_replace_template_variables` did not handle the placeholder.
    """
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _Config({}))

    folder_path, filename = import_paths.get_file_path_from_template_raw(
        "$artist/$album/$cdnum - $track - $title",
        {
            "artist": "Artist",
            "album": "Album",
            "title": "Song",
            "track_number": 5,
            "disc_number": 2,
            "total_discs": 2,
        },
    )

    assert folder_path == os.path.join("Artist", "Album")
    assert filename == "CD02 - 05 - Song"


def test_get_file_path_from_template_raw_collapses_cdnum_for_single_disc(monkeypatch):
    """$cdnum should expand to '' on single-disc albums so it disappears cleanly."""
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _Config({}))

    folder_path, filename = import_paths.get_file_path_from_template_raw(
        "$artist/$album/$cdnum - $track - $title",
        {
            "artist": "Artist",
            "album": "Album",
            "title": "Song",
            "track_number": 5,
            "disc_number": 1,
            "total_discs": 1,
        },
    )

    # No "CD01" prefix; trailing-dash regex collapses the empty placeholder.
    assert folder_path == os.path.join("Artist", "Album")
    assert filename == "05 - Song"


def test_get_file_path_from_template_raw_strips_cdnum_from_folders(monkeypatch):
    """Even if user puts $cdnum inside a folder segment, it gets stripped (defensive)."""
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: _Config({}))

    folder_path, filename = import_paths.get_file_path_from_template_raw(
        "$artist/$album/$cdnum/$track - $title",
        {
            "artist": "Artist",
            "album": "Album",
            "title": "Song",
            "track_number": 5,
            "disc_number": 1,
            "total_discs": 1,
        },
    )

    # Folder containing only $cdnum (which expands to empty for single-disc) gets dropped.
    assert folder_path == os.path.join("Artist", "Album")
    assert filename == "05 - Song"


def test_resolve_album_group_upgrades_standard_to_deluxe():
    album_naming.clear_album_grouping_cache()

    artist_context = {"name": "Cache Artist"}
    standard_album = {"album_name": "Cache Album"}
    deluxe_album = {"album_name": "Cache Album (Deluxe Edition)"}

    assert import_paths.resolve_album_group(artist_context, standard_album) == "Cache Album"
    assert import_paths.resolve_album_group(artist_context, deluxe_album) == "Cache Album (Deluxe Edition)"
    assert import_paths.resolve_album_group(artist_context, standard_album) == "Cache Album (Deluxe Edition)"


def test_build_final_path_for_track_uses_template_and_disc_folder(monkeypatch, tmp_path):
    config = _Config(
        {
            "soulseek.transfer_path": str(tmp_path / "Transfer"),
            "file_organization.enabled": True,
            "file_organization.templates": {
                "album_path": "$albumartist/$albumartist - $album/$track - $title",
                "single_path": "$artist/$artist - $title",
            },
            "file_organization.collab_artist_mode": "first",
            "file_organization.disc_label": "Disc",
        }
    )
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)

    calls = []

    def fake_get_album_tracks_for_source(source, album_id):
        calls.append((source, album_id))
        return {
            "items": [
                {"disc_number": 1},
                {"disc_number": 2},
            ]
        }

    monkeypatch.setattr(import_paths, "_get_album_tracks_for_source", fake_get_album_tracks_for_source)

    context = {
        "artist": {"name": "Artist One"},
        "album": {
            "name": "Album One",
            "id": "album-1",
            "release_date": "2026-01-01",
            "total_tracks": 12,
            "album_type": "album",
            "artists": [{"name": "Artist One"}],
        },
        "track_info": {
            "name": "Song One",
            "id": "track-1",
            "track_number": 4,
            "disc_number": 1,
            "artists": [{"name": "Artist One"}],
        },
        "original_search_result": {
            "title": "Song One",
            "clean_title": "Song One",
            "clean_album": "Album One",
            "clean_artist": "Artist One",
            "artists": [{"name": "Artist One"}],
        },
        "source": "deezer",
        "is_album_download": False,
    }

    final_path, created = import_paths.build_final_path_for_track(
        context,
        {"name": "Artist One"},
        {
            "is_album": True,
            "album_name": "Album One",
            "track_number": 4,
            "disc_number": 1,
        },
        ".flac",
    )

    assert created is True
    assert calls == [("deezer", "album-1")]
    assert final_path == str(
        tmp_path / "Transfer" / "Artist One" / "Artist One - Album One" / "Disc 1" / "04 - Song One.flac"
    )
    assert (tmp_path / "Transfer" / "Artist One" / "Artist One - Album One" / "Disc 1").is_dir()


def test_build_final_path_for_track_uses_track_disc_number_without_provider_lookup(monkeypatch, tmp_path):
    config = _Config(
        {
            "soulseek.transfer_path": str(tmp_path / "Transfer"),
            "file_organization.enabled": True,
            "file_organization.templates": {
                "album_path": "$albumartist/$albumartist - $album/$track - $title",
                "single_path": "$artist/$artist - $title",
            },
            "file_organization.collab_artist_mode": "first",
            "file_organization.disc_label": "Disc",
        }
    )
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)

    calls = []
    monkeypatch.setattr(
        import_paths,
        "_get_album_tracks_for_source",
        lambda source, album_id: calls.append((source, album_id)) or None,
    )

    context = {
        "artist": {"name": "Artist One"},
        "album": {
            "name": "Album One",
            "id": "album-1",
            "release_date": "2026-01-01",
            "total_tracks": 12,
            "album_type": "album",
            "artists": [{"name": "Artist One"}],
        },
        "track_info": {
            "name": "Song Two",
            "id": "track-2",
            "track_number": 4,
            "disc_number": 2,
            "artists": [{"name": "Artist One"}],
        },
        "original_search_result": {
            "title": "Song Two",
            "clean_title": "Song Two",
            "clean_album": "Album One",
            "clean_artist": "Artist One",
            "artists": [{"name": "Artist One"}],
        },
        "source": "deezer",
        "is_album_download": False,
    }

    final_path, created = import_paths.build_final_path_for_track(
        context,
        {"name": "Artist One"},
        {
            "is_album": True,
            "album_name": "Album One",
            "track_number": 4,
            "disc_number": 2,
        },
        ".flac",
    )

    assert created is True
    assert calls == []
    assert final_path == str(
        tmp_path / "Transfer" / "Artist One" / "Artist One - Album One" / "Disc 2" / "04 - Song Two.flac"
    )


def test_build_final_path_for_track_with_cdnum_template_skips_disc_folder(monkeypatch, tmp_path):
    """When the user template encodes the disc via $cdnum, the auto disc folder must not be added.

    Reported by user: multi-disc albums got both a "CDxx" label in the
    filename AND a redundant "Disc N" folder. The auto-folder should
    suppress when the template already encodes the disc.
    """
    config = _Config(
        {
            "soulseek.transfer_path": str(tmp_path / "Transfer"),
            "file_organization.enabled": True,
            "file_organization.templates": {
                "album_path": "$albumartist/$albumartist - $album/$cdnum - $track - $title",
                "single_path": "$artist/$artist - $title",
            },
            "file_organization.collab_artist_mode": "first",
            "file_organization.disc_label": "Disc",
        }
    )
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)
    monkeypatch.setattr(
        import_paths,
        "_get_album_tracks_for_source",
        lambda source, album_id: None,
    )

    context = {
        "artist": {"name": "Artist One"},
        "album": {
            "name": "Album One",
            "id": "album-1",
            "release_date": "2026-01-01",
            "total_tracks": 24,
            "total_discs": 2,
            "album_type": "album",
            "artists": [{"name": "Artist One"}],
        },
        "track_info": {
            "name": "Song Two",
            "id": "track-2",
            "track_number": 4,
            "disc_number": 2,
            "artists": [{"name": "Artist One"}],
        },
        "original_search_result": {
            "title": "Song Two",
            "clean_title": "Song Two",
            "clean_album": "Album One",
            "clean_artist": "Artist One",
            "artists": [{"name": "Artist One"}],
        },
        "source": "deezer",
        "is_album_download": False,
    }

    final_path, created = import_paths.build_final_path_for_track(
        context,
        {"name": "Artist One"},
        {
            "is_album": True,
            "album_name": "Album One",
            "track_number": 4,
            "disc_number": 2,
        },
        ".flac",
    )

    assert created is True
    # Filename has the CD02 label; NO "Disc 2" folder injected.
    assert final_path == str(
        tmp_path / "Transfer" / "Artist One" / "Artist One - Album One" / "CD02 - 04 - Song Two.flac"
    )
    # Verify the disc folder was not created either.
    assert not (tmp_path / "Transfer" / "Artist One" / "Artist One - Album One" / "Disc 2").exists()


# ── #745: $year must validate, never blind-slice release_date ──────────────

def test_extract_year_accepts_real_dates():
    assert import_paths._extract_year_from_release_date("2026-01-01") == "2026"
    assert import_paths._extract_year_from_release_date("1999") == "1999"
    assert import_paths._extract_year_from_release_date("2026") == "2026"
    # Datetime-ish string — still leads with a valid year.
    assert import_paths._extract_year_from_release_date("2010-12-31T00:00:00Z") == "2010"


def test_extract_year_rejects_non_date_values():
    # #745 exact case: release_date poisoned with the album NAME.
    assert import_paths._extract_year_from_release_date("Mantras (Deluxe)") == ""
    assert import_paths._extract_year_from_release_date("Mant") == ""
    # Implausible / sentinel years are rejected.
    assert import_paths._extract_year_from_release_date("0000") == ""
    assert import_paths._extract_year_from_release_date("1800") == ""
    assert import_paths._extract_year_from_release_date("9999") == ""
    # Empty / None.
    assert import_paths._extract_year_from_release_date("") == ""
    assert import_paths._extract_year_from_release_date(None) == ""
    # Fewer than 4 leading digits.
    assert import_paths._extract_year_from_release_date("202") == ""


def test_build_final_path_drops_garbage_year_from_folder(monkeypatch, tmp_path):
    """#745 reproduction: release_date carries the album NAME, not a date.
    The $year slot must resolve to empty and the bracket cleanup must drop the
    empty () — producing 'Album One' NOT 'Album One (Mant)'."""
    config = _Config(
        {
            "soulseek.transfer_path": str(tmp_path / "Transfer"),
            "file_organization.enabled": True,
            "file_organization.templates": {
                # Template that uses $year, like the reporter's.
                "album_path": "$albumartist/$album ($year) [$albumtype]/$track - $title",
                "single_path": "$artist/$artist - $title",
            },
            "file_organization.collab_artist_mode": "first",
            "file_organization.disc_label": "Disc",
        }
    )
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)
    monkeypatch.setattr(
        import_paths, "_get_album_tracks_for_source",
        lambda source, album_id: {"items": [{"disc_number": 1}]},
    )

    context = {
        "artist": {"name": "Katie Pruitt"},
        "album": {
            "name": "Mantras (Deluxe)",
            "id": "album-1",
            # POISONED: the album name landed in release_date (the #745 bug).
            "release_date": "Mantras (Deluxe)",
            "total_tracks": 12,
            "album_type": "album",
            "artists": [{"name": "Katie Pruitt"}],
        },
        "track_info": {
            "name": "White Lies, White Jesus And You",
            "id": "track-1",
            "track_number": 1,
            "disc_number": 1,
            "artists": [{"name": "Katie Pruitt"}],
        },
        "original_search_result": {
            "title": "White Lies, White Jesus And You",
            "clean_title": "White Lies, White Jesus And You",
            "clean_album": "Mantras (Deluxe)",
            "clean_artist": "Katie Pruitt",
            "artists": [{"name": "Katie Pruitt"}],
        },
        "source": "deezer",
        "is_album_download": False,
    }

    final_path, created = import_paths.build_final_path_for_track(
        context,
        {"name": "Katie Pruitt"},
        {"is_album": True, "album_name": "Mantras (Deluxe)", "track_number": 1, "disc_number": 1},
        ".flac",
    )

    assert created is True
    # The album folder must NOT contain "(Mant)" or any "(...)" year artifact.
    album_folder = os.path.basename(os.path.dirname(final_path))
    assert "(Mant" not in album_folder
    assert "()" not in album_folder
    # Empty () collapses; [Album] type stays.
    assert album_folder == "Mantras (Deluxe) [Album]"


def test_build_final_path_keeps_real_year_in_folder(monkeypatch, tmp_path):
    """Positive control: a genuine release_date still produces the (YYYY)
    folder — proves the guard didn't break the happy path."""
    config = _Config(
        {
            "soulseek.transfer_path": str(tmp_path / "Transfer"),
            "file_organization.enabled": True,
            "file_organization.templates": {
                "album_path": "$albumartist/$album ($year) [$albumtype]/$track - $title",
                "single_path": "$artist/$artist - $title",
            },
            "file_organization.collab_artist_mode": "first",
            "file_organization.disc_label": "Disc",
        }
    )
    monkeypatch.setattr(import_paths, "_get_config_manager", lambda: config)
    monkeypatch.setattr(
        import_paths, "_get_album_tracks_for_source",
        lambda source, album_id: {"items": [{"disc_number": 1}]},
    )

    context = {
        "artist": {"name": "Katie Pruitt"},
        "album": {
            "name": "Mantras (Deluxe)",
            "id": "album-1",
            "release_date": "2026-04-12",
            "total_tracks": 12,
            "album_type": "album",
            "artists": [{"name": "Katie Pruitt"}],
        },
        "track_info": {
            "name": "White Lies, White Jesus And You",
            "id": "track-1",
            "track_number": 1,
            "disc_number": 1,
            "artists": [{"name": "Katie Pruitt"}],
        },
        "original_search_result": {
            "title": "White Lies, White Jesus And You",
            "clean_title": "White Lies, White Jesus And You",
            "clean_album": "Mantras (Deluxe)",
            "clean_artist": "Katie Pruitt",
            "artists": [{"name": "Katie Pruitt"}],
        },
        "source": "deezer",
        "is_album_download": False,
    }

    final_path, _ = import_paths.build_final_path_for_track(
        context,
        {"name": "Katie Pruitt"},
        {"is_album": True, "album_name": "Mantras (Deluxe)", "track_number": 1, "disc_number": 1},
        ".flac",
    )

    album_folder = os.path.basename(os.path.dirname(final_path))
    assert album_folder == "Mantras (Deluxe) (2026) [Album]"
