#!/usr/bin/env python3

# Unit tests for the Renderer abstraction.
# 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.

"""Unit tests for renderer.Dep5Renderer.

These pin each rendered piece in isolation; they must agree with the
generate_output characterization tests in output_test.py, which lock the
equivalent text at the (still-live) orchestration the renderer will replace.
"""

import contextlib
import io
import unittest

import debian.copyright as dc

from decopy import cmdoptions, dep5, nodes
from decopy.core_types import CopyrightHolder, License
from decopy.renderer import Dep5Renderer, Renderer, renderer_for

_CURRENT_FORMAT = (
    "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n"
)


def _options(args):
    base = ["--no-progress", "--root", "tests/testdata/one"]
    return cmdoptions.process_options([*base, *args])


class HeaderTest(unittest.TestCase):
    def setUp(self):
        self.renderer = Dep5Renderer()

    def test_full_mode_without_copyright(self):
        header = self.renderer.header(None, _options([]))
        self.assertEqual(header, _CURRENT_FORMAT.rstrip("\n") + "\nSource: TODO")

    def test_partial_mode_lists_files(self):
        options = cmdoptions.process_options(["--mode=partial", "."])
        header = self.renderer.header(None, options)
        self.assertEqual(
            header,
            _CURRENT_FORMAT.rstrip("\n") + "\nSource: TODO\nComment: *** only: . ***",
        )

    def test_existing_copyright_overrides_format_keeps_fields(self):
        text = (
            "Format: https://example.org/old-format\n"
            "Upstream-Name: example\n"
            "Source: https://example.org/dl\n"
        )
        with contextlib.redirect_stderr(io.StringIO()):
            copyright_ = dep5.Copyright(io.StringIO(text), strict=False)
        header = self.renderer.header(copyright_, _options([]))
        self.assertEqual(
            header,
            _CURRENT_FORMAT.rstrip("\n")
            + "\nUpstream-Name: example\nSource: https://example.org/dl",
        )


class FileBlockTest(unittest.TestCase):
    def setUp(self):
        self._saved_licenses = License.licenses
        License.licenses = {}
        self.renderer = Dep5Renderer()

    def tearDown(self):
        License.licenses = self._saved_licenses

    def _group(self):
        root = nodes.DirInfo(parent=None, name="")
        group = dep5.Group(position=0)
        f = nodes.FileInfo(parent=root, name="README")
        f.add_licenses(["MIT"])
        f.add_copyrights([CopyrightHolder("Alice", "alice@x")])
        group.add_file(f)
        return group

    def test_file_block_renders_dep5_files_paragraph(self):
        options = _options(["--no-glob"])
        grp = self._group()
        block = self.renderer.file_block(grp, options, grp.files.sorted_members())
        self.assertEqual(
            block,
            "Files: README\nCopyright: Alice <alice@x>\nLicense: MIT",
        )


class LicenseBlockTest(unittest.TestCase):
    def setUp(self):
        self._saved_licenses = License.licenses
        License.licenses = {}
        self.renderer = Dep5Renderer()

    def tearDown(self):
        License.licenses = self._saved_licenses

    def test_no_stored_text_emits_placeholder(self):
        block = self.renderer.license_block("MIT", _options([]))
        self.assertEqual(
            block, "License: MIT\nComment: Add the corresponding license text here"
        )

    def test_stored_text_is_included(self):
        stored = dc.License(synopsis="MIT", text="Permission is granted ...")
        License.get("MIT").stored = dc.LicenseParagraph.create(stored)
        block = self.renderer.license_block("MIT", _options([]))
        self.assertTrue(block.startswith("License: MIT"))
        self.assertIn("Permission is granted", block)

    def test_spdx_converts_display_name(self):
        block = self.renderer.license_block("GPL-2", _options(["--spdx"]))
        self.assertTrue(block.startswith("License: GPL-2.0-only\n"))

    def test_no_spdx_keeps_dep5_name(self):
        block = self.renderer.license_block("GPL-2", _options(["--no-spdx"]))
        self.assertTrue(block.startswith("License: GPL-2\n"))


class ProtocolConformanceTest(unittest.TestCase):
    def test_dep5renderer_is_a_renderer(self):
        self.assertIsInstance(Dep5Renderer(), Renderer)


class RendererForTest(unittest.TestCase):
    def test_selects_dep5_renderer(self):
        self.assertIsInstance(renderer_for(_options([])), Dep5Renderer)

    def test_returns_a_renderer(self):
        self.assertIsInstance(renderer_for(_options([])), Renderer)


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