"""Unified Machine model - combines equipment and PCs."""

from shopdb.extensions import db
from .base import BaseModel, SoftDeleteMixin, AuditMixin


class MachineType(BaseModel):
    """
    Machine type classification.
    Categories: Equipment, PC, Network, Printer
    """
    __tablename__ = 'machinetypes'

    machinetypeid = db.Column(db.Integer, primary_key=True)
    machinetype = db.Column(db.String(100), unique=True, nullable=False)
    category = db.Column(
        db.String(50),
        nullable=False,
        default='Equipment',
        comment='Equipment, PC, Network, or Printer'
    )
    description = db.Column(db.Text)
    icon = db.Column(db.String(50), comment='Icon name for UI')

    def __repr__(self):
        return f"<MachineType {self.machinetype}>"


class MachineStatus(BaseModel):
    """Machine status options."""
    __tablename__ = 'machinestatuses'

    statusid = db.Column(db.Integer, primary_key=True)
    status = db.Column(db.String(50), unique=True, nullable=False)
    description = db.Column(db.Text)
    color = db.Column(db.String(20), comment='CSS color for UI')

    def __repr__(self):
        return f"<MachineStatus {self.status}>"


class PCType(BaseModel):
    """
    PC type classification for more specific PC categorization.
    Examples: Shopfloor PC, Engineer Workstation, CMM PC, etc.
    """
    __tablename__ = 'pctypes'

    pctypeid = db.Column(db.Integer, primary_key=True)
    pctype = db.Column(db.String(100), unique=True, nullable=False)
    description = db.Column(db.Text)

    def __repr__(self):
        return f"<PCType {self.pctype}>"


class Machine(BaseModel, SoftDeleteMixin, AuditMixin):
    """
    Unified machine model for all asset types.

    Machine types can be:
    - CNC machines, CMMs, EDMs, etc. (manufacturing equipment)
    - PCs (shopfloor PCs, engineer workstations, etc.)
    - Network devices (servers, switches, etc.) - if network_devices plugin not used

    The machinetype.category field distinguishes between types.
    """
    __tablename__ = 'machines'

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

    # Identification
    machinenumber = db.Column(
        db.String(50),
        unique=True,
        nullable=False,
        index=True,
        comment='Business identifier (e.g., CMM01, G5QX1GT3ESF)'
    )
    alias = db.Column(
        db.String(100),
        comment='Friendly name'
    )
    hostname = db.Column(
        db.String(100),
        index=True,
        comment='Network hostname (for PCs)'
    )
    serialnumber = db.Column(
        db.String(100),
        index=True,
        comment='Hardware serial number'
    )

    # Classification
    machinetypeid = db.Column(
        db.Integer,
        db.ForeignKey('machinetypes.machinetypeid'),
        nullable=False
    )
    pctypeid = db.Column(
        db.Integer,
        db.ForeignKey('pctypes.pctypeid'),
        nullable=True,
        comment='Set for PCs, NULL for equipment'
    )
    businessunitid = db.Column(
        db.Integer,
        db.ForeignKey('businessunits.businessunitid'),
        nullable=True
    )
    modelnumberid = db.Column(
        db.Integer,
        db.ForeignKey('models.modelnumberid'),
        nullable=True
    )
    vendorid = db.Column(
        db.Integer,
        db.ForeignKey('vendors.vendorid'),
        nullable=True
    )

    # Status
    statusid = db.Column(
        db.Integer,
        db.ForeignKey('machinestatuses.statusid'),
        default=1,
        comment='In Use, Spare, Retired, etc.'
    )

    # Location and mapping
    locationid = db.Column(
        db.Integer,
        db.ForeignKey('locations.locationid'),
        nullable=True
    )
    mapleft = db.Column(db.Integer, comment='X coordinate on floor map')
    maptop = db.Column(db.Integer, comment='Y coordinate on floor map')
    islocationonly = db.Column(
        db.Boolean,
        default=False,
        comment='Virtual location marker (not actual machine)'
    )

    # PC-specific fields (nullable for non-PC machines)
    osid = db.Column(
        db.Integer,
        db.ForeignKey('operatingsystems.osid'),
        nullable=True
    )
    loggedinuser = db.Column(db.String(100), nullable=True)
    lastreporteddate = db.Column(db.DateTime, nullable=True)
    lastboottime = db.Column(db.DateTime, nullable=True)

    # Features/flags
    isvnc = db.Column(db.Boolean, default=False, comment='VNC remote access enabled')
    iswinrm = db.Column(db.Boolean, default=False, comment='WinRM enabled')
    isshopfloor = db.Column(db.Boolean, default=False, comment='Shopfloor PC')
    requiresmanualconfig = db.Column(
        db.Boolean,
        default=False,
        comment='Multi-PC machine needs manual configuration'
    )

    # Notes
    notes = db.Column(db.Text, nullable=True)

    # Relationships
    machinetype = db.relationship('MachineType', backref='machines')
    pctype = db.relationship('PCType', backref='machines')
    businessunit = db.relationship('BusinessUnit', backref='machines')
    model = db.relationship('Model', backref='machines')
    vendor = db.relationship('Vendor', backref='machines')
    status = db.relationship('MachineStatus', backref='machines')
    location = db.relationship('Location', backref='machines')
    operatingsystem = db.relationship('OperatingSystem', backref='machines')

    # Communications (one-to-many)
    communications = db.relationship(
        'Communication',
        backref='machine',
        cascade='all, delete-orphan',
        lazy='dynamic'
    )

    # Installed applications (for PCs)
    installedapps = db.relationship(
        'InstalledApp',
        back_populates='machine',
        cascade='all, delete-orphan',
        lazy='dynamic'
    )

    # Indexes
    __table_args__ = (
        db.Index('idx_machine_type_bu', 'machinetypeid', 'businessunitid'),
        db.Index('idx_machine_location', 'locationid'),
        db.Index('idx_machine_active', 'isactive'),
        db.Index('idx_machine_hostname', 'hostname'),
    )

    def __repr__(self):
        return f"<Machine {self.machinenumber}>"

    @property
    def display_name(self):
        """Get display name (alias if set, otherwise machinenumber)."""
        return self.alias or self.machinenumber

    @property
    def derived_machinetype(self):
        """Get machinetype from model (single source of truth)."""
        if self.model and self.model.machinetype:
            return self.model.machinetype
        return None

    @property
    def is_pc(self):
        """Check if this machine is a PC type."""
        mt = self.derived_machinetype
        return mt.category == 'PC' if mt else False

    @property
    def is_equipment(self):
        """Check if this machine is equipment."""
        mt = self.derived_machinetype
        return mt.category == 'Equipment' if mt else False

    @property
    def is_network_device(self):
        """Check if this machine is a network device."""
        mt = self.derived_machinetype
        return mt.category == 'Network' if mt else False

    @property
    def is_printer(self):
        """Check if this machine is a printer."""
        mt = self.derived_machinetype
        return mt.category == 'Printer' if mt else False

    @property
    def primary_ip(self):
        """Get primary IP address from communications."""
        comm = self.communications.filter_by(
            isprimary=True,
            comtypeid=1  # IP type
        ).first()
        if comm:
            return comm.ipaddress
        # Fall back to any IP
        comm = self.communications.filter_by(comtypeid=1).first()
        return comm.ipaddress if comm else None
