#!/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.

"""Resolve a License stanza body for a detected licence name.

Given a single stanza key (one licence, optionally a "<base> with <label>
exception"), produce the plain-text body for its DEP-5 License paragraph, or
None when nothing is curated (the renderer then keeps its TODO placeholder).
The body is plain text with blank-line-separated paragraphs; turning it into the
indented DEP-5 shape is the renderer's job.

The lookup tries, on the base licence: a /usr/share/common-licenses reference
(prefixed with a variant-notice header when one ships), else an embedded full
text for a short licence not in common-licenses, else None.
"""

from __future__ import annotations

import textwrap

from decopy.licensing.data.common_licenses import CommonLicense, common_license_for
from decopy.licensing.data.loader import (
    embedded_exception,
    embedded_license,
    variant_header,
)

_EXCEPTION_SUFFIX = " exception"
_WITH = " with "

# Wrap generated prose to this width. It is the customary 78-column line target
# minus the renderer's single-space DEP-5 continuation indent, so a rendered
# body line stays within 78 columns. Verbatim embedded texts are not wrapped.
WRAP_WIDTH = 77


def _wrap(prose: str) -> str:
    """Wrap decopy-generated prose to the DEP-5 body width.

    Blank-line paragraph breaks are preserved; long tokens (paths, URLs) are
    never split, so a line may exceed the width when a single token does.
    """
    return "\n\n".join(
        "\n".join(
            textwrap.wrap(
                paragraph,
                width=WRAP_WIDTH,
                break_long_words=False,
                break_on_hyphens=False,
            )
        )
        for paragraph in prose.split("\n\n")
    )


def _split_exception(key: str) -> tuple[str, str | None]:
    """Split "<base> with <label> exception" into (base, label).

    Returns (key, None) when the key carries no exception.
    """
    if key.endswith(_EXCEPTION_SUFFIX) and _WITH in key:
        base, _, rest = key.partition(_WITH)
        return base.strip(), rest[: -len(_EXCEPTION_SUFFIX)].strip()
    return key, None


def _reference_text(common: CommonLicense) -> str:
    return _wrap(
        f"On Debian systems, the complete text of {common.friendly_name} "
        f"can be found in `/usr/share/common-licenses/{common.file}'."
    )


def _exception_note(label: str) -> str:
    """Placeholder for an exception whose text decopy does not bundle."""
    return _wrap(f"TODO: add the text of the {label} exception here.")


def _with_exception(base_body: str | None, label: str) -> str:
    """Append the exception block (embedded text, else a TODO note) to the base
    body. When the base body is None, the block stands alone so the stanza is
    never empty."""
    block = embedded_exception(label) or _exception_note(label)
    if base_body is None:
        return block
    return f"{base_body}\n\n{block}"


class LicenseTextResolver:
    """Maps a stanza key to its License paragraph body, or None."""

    def body(self, key: str) -> str | None:
        base, exception = _split_exception(key)
        base_body = self._base_body(base)
        if exception is None:
            return base_body
        return _with_exception(base_body, exception)

    def _base_body(self, base: str) -> str | None:
        common = common_license_for(base)
        if common is None:
            # Not in common-licenses: use an embedded full text (verbatim).
            return embedded_license(base)
        # The header (keyed on the full variant name, e.g. GPL-2 vs GPL-2+)
        # disambiguates the reference; it is prose, so wrap it too.
        header = variant_header(base)
        reference = _reference_text(common)
        if header:
            return f"{_wrap(header)}\n\n{reference}"
        return reference
