"""Data models for the depwhy dependency constraint graph and conflict reporting."""

from __future__ import annotations

from dataclasses import dataclass, field


@dataclass(frozen=True)
class Constraint:
    """Represents a single version constraint imposed on a package by a dependent.

    Attributes:
        package: The name of the package being constrained, e.g. "urllib3".
        specifier: The PEP 440 version specifier string, e.g. ">=1.21.1,<1.27".
        required_by: The dependent that imposes this constraint, e.g. "requests==2.28.0".
        chain: The full dependency chain leading to this constraint,
               e.g. ["your-project", "requests==2.28.0", "urllib3>=1.21.1,<1.27"].
    """

    package: str
    specifier: str
    required_by: str
    chain: list[str] = field(default_factory=lambda: [])


@dataclass(frozen=True)
class FixCandidate:
    """A candidate fix for a dependency conflict, including a human-readable description
    and a ready-to-run pip command.

    Attributes:
        description: A plain-English description of the fix,
                     e.g. "Remove your pin — use urllib3==1.26.18 (latest compatible)".
        pip_command: A copy-pasteable pip install command that resolves the conflict,
                     e.g. "pip install urllib3==1.26.18".
        is_alternative: False if this is the top recommended fix; True if it is a fallback option.
    """

    description: str
    pip_command: str
    is_alternative: bool


@dataclass(frozen=True)
class Conflict:
    """Describes a single unresolvable dependency conflict, including all constraints
    that contribute to it and ranked fix suggestions.

    Attributes:
        package: The name of the package that cannot be resolved, e.g. "urllib3".
        problem: A plain-English one-liner describing the conflict.
        constraints: All version constraints on this package that together cause the conflict.
        solution: The top recommended fix candidate.
        alternative: A second-best fix candidate; may be None if no alternative exists.
    """

    package: str
    problem: str
    constraints: list[Constraint]
    solution: FixCandidate
    alternative: FixCandidate | None


@dataclass(frozen=True)
class ConflictReport:
    """The top-level result returned by analyze(), summarising all detected conflicts.

    Attributes:
        has_conflicts: True if at least one hard conflict was detected.
        conflicts: List of all detected hard conflicts.
        warnings: Human-readable strings describing soft warnings (narrowed-but-valid
                  version ranges that are not hard failures).
        analyzed_packages: Total number of packages examined during analysis.
    """

    has_conflicts: bool
    conflicts: list[Conflict]
    warnings: list[str]
    analyzed_packages: int
