"""What a bay actually HAS: one row per printer queue a PC reported.

This is the observed half of the printer loop. The assigned half already exists
as usesprinter/defaultprinter relationship rows on the machine, and the two are
kept apart on purpose: the moment a drifted bay's observed state is allowed to
write assignment rows, enforcement stops meaning anything. Nothing in this table
is desired state, and no code may promote it to desired state without a person
asking for that explicitly.

Current state, not history. The latest report for a host REPLACES every row that
host had before, so "what does this bay have" is a plain filter and never a
question about time. An append-only table would grow with every GE-Enforce cycle
and answer that question wrong. The audit log already records each ingest, which
is where the history lives.

Rows are keyed by hostname as reported, with assetid as a resolved convenience:
a bay can report before anyone creates its computer record, and the report must
still land. Matching an observed queue back to a ShopDB printer asset happens at
READ time (port address first, then queue name) so a printer added tomorrow
matches without the bay re-reporting.

Replacement is a hard DELETE of the host's rows, not a soft one: the inherited
isactive flag is not a soft-delete marker here, because a queue that is gone
from the bay is not observed state that has been retired, it is state that was
never observed again.
"""

from shopdb.api import db, BaseModel


class PrinterObservedQueue(BaseModel):
    """One Windows print queue seen on one reporting PC at one point in time."""

    __tablename__ = 'printerobservedqueues'

    printerobservedqueueid = db.Column(db.Integer, primary_key=True)

    # The reporting PC, resolved at ingest. Nullable because an unenrolled bay
    # still gets to report, and no backref: core assets must not grow a
    # dependency on this plugin.
    assetid = db.Column(
        db.Integer,
        db.ForeignKey('assets.assetid', ondelete='CASCADE'),
        nullable=True,
        comment='Reporting PC asset, resolved from hostname at ingest',
    )
    asset = db.relationship('Asset', lazy='select', viewonly=True)

    # Authoritative identity of the report, stored as sent. assetid can be null
    # or can go stale after a rename; hostname is what the replace keys on.
    hostname = db.Column(
        db.String(255),
        nullable=False,
        comment='Reporting PC hostname, as sent by the collector',
    )

    queuename = db.Column(
        db.String(255),
        nullable=False,
        comment='Windows printer (queue) name',
    )
    drivername = db.Column(
        db.String(255),
        comment='Windows driver name, verbatim; comparable to printerdrivers.drivername',
    )
    portname = db.Column(
        db.String(255),
        comment='Windows port name, freeform',
    )
    # Primary match key: an IP or FQDN identifies a device unambiguously, where
    # a queue name is only ever a convention. Null for non-TCP/IP ports.
    portaddress = db.Column(
        db.String(255),
        comment='Host address the port points at (IP or FQDN)',
    )

    isdefault = db.Column(
        db.Boolean,
        nullable=False,
        default=False,
        comment='Was the default queue for the reporting context',
    )
    isshared = db.Column(
        db.Boolean,
        nullable=False,
        default=False,
        comment='Queue is shared off this PC',
    )

    # Server-stamped once per report, so every row of one report carries the
    # same value and "when did this bay last report" needs no aggregate.
    observedat = db.Column(
        db.DateTime,
        nullable=False,
        comment='When the report that produced this row was ingested',
    )

    __table_args__ = (
        # Windows queue names are unique per host, so this doubles as the read
        # index for the hostname filter and turns a botched partial replace into
        # an IntegrityError instead of silent duplicate queues.
        db.UniqueConstraint('hostname', 'queuename',
                            name='uq_printerobservedqueue_host_queue'),
        db.Index('idx_printerobservedqueues_portaddress', 'portaddress'),
        db.Index('idx_printerobservedqueues_assetid', 'assetid'),
    )

    def __repr__(self):
        return f"<PrinterObservedQueue {self.hostname}:{self.queuename}>"
