"""
Per-package detail for a Vulnerability.

Wazuh + Nessus can both report the same CVE affecting MULTIPLE packages
on the same asset (e.g. CVE-2023-48795 hits both PuTTY 0.73 and WinSCP
6.1.2 on the same host). Until migration 023 we merged all packages
into a single comma-joined string on `vulnerabilities.package_name` —
which meant per-package `fixed_version` was impossible.

A `VulnerabilityPackage` row is one entry in the affected-packages list
for a single `Vulnerability`. `fixed_version` is per-package (Wazuh /
Nessus / Vulnrichment-extracted), so the UI can render
"PATCH AVAILABLE" per package and show packages individually instead
of a joined string.

The parent `Vulnerability` keeps `package_name` / `package_version` /
`fixed_version` as a SUMMARY (first detected package, or the most
severe one) so legacy queries + the list view stay fast without a JOIN.
"""
from datetime import datetime
from sqlalchemy import (
    Column, Integer, String, DateTime, ForeignKey, UniqueConstraint, Index,
)
from sqlalchemy.orm import relationship, backref

from app.models.base import Base, TimestampMixin


class VulnerabilityPackage(Base, TimestampMixin):
    __tablename__ = "vulnerability_packages"

    id = Column(Integer, primary_key=True)
    vulnerability_id = Column(
        Integer,
        ForeignKey("vulnerabilities.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    package_name = Column(String(255), nullable=False, index=True)
    package_version = Column(String(100), nullable=True)
    fixed_version = Column(String(100), nullable=True)

    # Source tag — which scanner reported this package row.
    # ("wazuh" / "nessus" / "manual"). Multi-scanner deployments
    # can show per-source confirmation in the per-package view.
    source = Column(String(20), nullable=True)

    first_detected_at = Column(DateTime, nullable=False, default=datetime.utcnow)
    last_seen_at = Column(DateTime, nullable=False, default=datetime.utcnow)

    vulnerability = relationship(
        "Vulnerability",
        backref=backref(
            "packages",
            cascade="all, delete-orphan",
            passive_deletes=True,
        ),
    )

    __table_args__ = (
        UniqueConstraint(
            "vulnerability_id", "package_name",
            name="uq_vulnpkg_vuln_package",
        ),
        Index("ix_vulnpkg_name_fixed", "package_name", "fixed_version"),
    )

    def __repr__(self) -> str:
        return (
            f"<VulnerabilityPackage(vuln={self.vulnerability_id}, "
            f"pkg='{self.package_name}', ver='{self.package_version}', "
            f"fix='{self.fixed_version}')>"
        )

    @property
    def has_fix(self) -> bool:
        """True iff a fixed_version is known AND newer than package_version.
        Equality (fixed == installed) returns False — covers the
        Ghostscript inclusive-upper-bound case where the supposed fix
        is the affected version itself."""
        if not self.fixed_version:
            return False
        if not self.package_version:
            return True  # fix known, install unknown — best to show "patch available"
        return self.fixed_version != self.package_version
