#!/usr/bin/env python3
# Copyright: 2026, Maximiliano Curia <maxy@debian.org>
#
# License: ISC
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#  .
#  THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
#  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
#  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
#  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
#  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
#  PERFORMANCE OF THIS SOFTWARE.

"""Tests for the LicenseTextResolver stanza-body ladder."""

from __future__ import annotations

from decopy.licensing.license_texts import WRAP_WIDTH, LicenseTextResolver


def test_reference_gpl2():
    body = LicenseTextResolver().body("GPL-2")
    assert body is not None
    assert "`/usr/share/common-licenses/GPL-2'" in body
    # The friendly name may span a wrap break, so compare whitespace-normalised.
    assert "the GNU General Public License version 2" in " ".join(body.split())


def test_reference_or_later_uses_same_file():
    body = LicenseTextResolver().body("GPL-2+")
    assert body is not None
    assert "`/usr/share/common-licenses/GPL-2'" in body


def test_none_for_uncurated():
    # Neither in the common-licenses map nor the embedded set.
    assert LicenseTextResolver().body("Beerware") is None


def test_embedded_full_text():
    body = LicenseTextResolver().body("Expat")
    assert body is not None
    assert "Permission is hereby granted, free of charge" in body
    # An embedded text, not a common-licenses reference.
    assert "/usr/share/common-licenses" not in body


def test_embedded_text_is_not_wrapped():
    # Embedded texts are authoritative and kept verbatim (unlike generated
    # prose), so their original long lines are preserved.
    body = LicenseTextResolver().body("Expat")
    assert body is not None
    assert any(len(line) > WRAP_WIDTH for line in body.split("\n"))


def test_reference_none_for_markers():
    resolver = LicenseTextResolver()
    assert resolver.body("public-domain") is None
    assert resolver.body("Unknown") is None


def test_reference_wrapped_to_content_width():
    body = LicenseTextResolver().body("GPL-2")
    assert body is not None
    # 78-column target minus the renderer's single-space continuation indent.
    assert all(len(line) <= WRAP_WIDTH for line in body.split("\n"))
    # It really wrapped (the sentence does not fit on one line).
    assert "\n" in body


def test_reference_path_not_split():
    body = LicenseTextResolver().body("GPL-2")
    assert body is not None
    assert any(
        "`/usr/share/common-licenses/GPL-2'." in line for line in body.split("\n")
    )


def test_reference_content_preserved():
    body = LicenseTextResolver().body("GPL-2")
    assert body is not None
    # The reference sentence is the last block, after the header.
    reference = body.split("\n\n")[-1]
    joined = " ".join(line.strip() for line in reference.split("\n"))
    assert joined == (
        "On Debian systems, the complete text of the GNU General Public "
        "License version 2 can be found in `/usr/share/common-licenses/GPL-2'."
    )


def test_header_prepended_before_reference():
    body = LicenseTextResolver().body("GPL-2")
    assert body is not None
    normalised = " ".join(body.split())
    assert "This program is free software" in normalised
    # The header comes before the common-licenses reference.
    assert body.index("This program is free software") < body.index(
        "/usr/share/common-licenses/GPL-2"
    )


def test_only_and_or_later_headers_differ():
    only = LicenseTextResolver().body("GPL-2")
    later = LicenseTextResolver().body("GPL-2+")
    assert only is not None
    assert later is not None
    # The or-later notice grants "any later version"; the only one does not.
    assert "any later version" not in " ".join(only.split())
    assert "any later version" in " ".join(later.split())
    # Both still reference the same common-licenses file.
    assert "/usr/share/common-licenses/GPL-2" in only
    assert "/usr/share/common-licenses/GPL-2" in later


def test_reference_without_header_is_reference_only():
    # BSL-1 is in the common-licenses map but has no generated header.
    body = LicenseTextResolver().body("BSL-1")
    assert body is not None
    assert "/usr/share/common-licenses/BSL-1.0" in body
    assert "This program is free software" not in body


def test_header_body_lines_within_width():
    body = LicenseTextResolver().body("GPL-2")
    assert body is not None
    assert all(len(line) <= WRAP_WIDTH for line in body.split("\n"))


def test_with_exception_full():
    body = LicenseTextResolver().body("GPL-2+ with AutoConf exception")
    assert body is not None
    # base body: the GPL-2 common-licenses reference + "version 2 or later" header
    assert "`/usr/share/common-licenses/GPL-2'" in body
    assert "or (at your option) any later version" in " ".join(body.split())
    # appended verbatim exception text, blank-line separated from the base
    assert "As a special exception" in body
    assert "output of Autoconf" in body


def test_with_exception_uncurated_keeps_base_and_notes_label():
    body = LicenseTextResolver().body("GPL-2 with Nonesuch exception")
    assert body is not None
    assert "`/usr/share/common-licenses/GPL-2'" in body  # base body kept
    assert "Nonesuch" in body  # the missing exception is named, not dropped


def test_with_exception_uncurated_base_only_still_emits_block():
    # A base with no curated body (Beerware) plus an uncurated exception still
    # produces a non-empty stanza naming the exception.
    body = LicenseTextResolver().body("Beerware with Nonesuch exception")
    assert body is not None
    assert "Nonesuch" in body


def test_embedded_exception_text_not_wrapped():
    # The exception text is verbatim (like embedded licences), not reflowed.
    body = LicenseTextResolver().body("GPL-2 with AutoConf exception")
    assert body is not None
    assert any(len(line) > WRAP_WIDTH for line in body.split("\n"))
