#!/usr/bin/env python3
#
# Copyright: 2016, 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.

from __future__ import annotations

import sys
import typing as t
from collections.abc import Generator, Mapping
from contextlib import contextmanager
from pathlib import Path
from typing import IO

from .copyright_processor import Copyright
from .core_types import Options
from .group import TallyTracker
from .group_contracts import GroupProtocol
from .log import logger
from .renderer import Dep5Renderer, Renderer
from .tree import RootInfo


@contextmanager
def open_output_file(filename: str) -> Generator[IO[str], None, None]:
    if filename:
        with Path(filename).open("wt", encoding="utf-8") as f:
            yield f
    else:
        yield sys.stdout


def _tally_partial_parents(
    filetree: RootInfo, options: Options, tracker: TallyTracker
) -> None:
    """In partial mode, mark the requested files' parents as tallied.

    This avoids emitting a 'Files: *' paragraph that would claim the whole tree.
    """
    if options.mode != "partial":
        return
    for item in options.files:
        parent = filetree[item].parent
        if not parent:
            continue
        tracker.tally(parent)


def generate_output(
    groups: Mapping[t.Any, GroupProtocol],
    filetree: RootInfo,
    copyright_: Copyright | None,
    options: Options,
    renderer: Renderer | None = None,
) -> None:
    if renderer is None:
        renderer = Dep5Renderer()

    # Track the licenses that are in use
    licenses: set[str] = set()

    # Shared across all groups so a later, more specific group does not
    # re-wildcard an ancestor an earlier group already covered.
    tracker = TallyTracker()

    with open_output_file(options.output) as f:
        # Print header
        header = renderer.header(copyright_, options)
        print(header, file=f)
        if options.output:
            logger.debug("Generated header:\n%s", header)

        _tally_partial_parents(filetree, options, tracker)

        # Print files paragraphs
        for _, group in sorted(groups.items(), key=lambda i: i[1].sort_key(options)):
            if not group.copyright_block_valid():
                continue

            licenses.update(group.licenses.keys())

            if options.glob:
                file_patterns = group.files.get_patterns(tracker)
            else:
                # When called with --no-glob, only list the file names
                file_patterns = group.files.sorted_members()

            paragraph = renderer.file_block(group, options, file_patterns)

            print(file=f)
            print(paragraph, file=f)

            if options.output:
                logger.debug("Generated group:\n%s", paragraph)

        # Print license paragraphs
        for key in sorted(licenses):
            paragraph = renderer.license_block(key, options)

            print(file=f)
            print(paragraph, file=f)

            if options.output:
                logger.debug("Generated license block:\n%s", paragraph)
