"""Measuring-tools models.

Measuring tools are gage-lab instruments (calipers, micrometers, thread gages,
bore gages, height gages, Genspect heads, ...) that measure parts, as opposed
to machines that make parts (see ADR-005). Each tool is a core Asset plus a
one-to-one measuringtools extension row.

The lifecycle a measuring tool cares about is CALIBRATION, not maintenance:
an interval, a last date, and a next date. Calibration STATUS is DERIVED from
nextcalibrationdate at read time (see derive_status), never stored, so it is
always current no matter how long since the last write. This mirrors the
warranty plugin's derive-at-read pattern.
"""

from datetime import date, timedelta

from shopdb.api import db, BaseModel

# Window before nextcalibrationdate where a tool counts as "due soon".
DUESOON_WINDOW_DAYS = 30

# Derived status -> display color (hex). Reused by the frontend status badge.
STATUS_COLORS = {
    'overdue': '#F44336',
    'duesoon': '#FF9800',
    'current': '#4CAF50',
    'unknown': '#9E9E9E',
}


def derive_status(nextcalibrationdate, today=None):
    """Calibration status from a next-calibration date. Never stored.

    overdue  - the next date is in the past
    duesoon  - the next date is within DUESOON_WINDOW_DAYS from today
    current  - the next date is further out than the window
    unknown  - no next date recorded
    """
    if not nextcalibrationdate:
        return 'unknown'
    today = today or date.today()
    if nextcalibrationdate < today:
        return 'overdue'
    if nextcalibrationdate <= today + timedelta(days=DUESOON_WINDOW_DAYS):
        return 'duesoon'
    return 'current'


class MeasuringToolType(BaseModel):
    """Measuring-tool classification (Caliper, Micrometer, Thread Gage, ...).

    Site-managed lookup with a display color for badges and map markers,
    the same shape as the machine/computer/printer type tables.
    """
    __tablename__ = 'measuringtooltypes'

    measuringtooltypeid = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), unique=True, nullable=False)
    description = db.Column(db.Text)
    color = db.Column(db.String(20), comment='CSS color for UI/map markers')

    def __repr__(self):
        return f"<MeasuringToolType {self.name}>"


class MeasuringTool(BaseModel):
    """Measuring-tool extension data, one-to-one with a core Asset.

    Identity lives on the Asset (assetnumber = gage tag, serialnumber = vendor
    serial, gaugelabreference identifier). This row carries only the metrology
    domain fields: the tool type and the calibration lifecycle.
    """
    __tablename__ = 'measuringtools'

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

    # Link to the core asset (one extension row per asset).
    assetid = db.Column(
        db.Integer,
        db.ForeignKey('assets.assetid', ondelete='CASCADE'),
        unique=True,
        nullable=False,
        index=True,
    )

    measuringtooltypeid = db.Column(
        db.Integer,
        db.ForeignKey('measuringtooltypes.measuringtooltypeid'),
        nullable=True,
    )

    # Calibration lifecycle. Status is DERIVED from nextcalibrationdate, so
    # none of these columns store a status.
    calibrationintervaldays = db.Column(db.Integer, nullable=True)
    lastcalibrationdate = db.Column(db.Date, nullable=True)
    nextcalibrationdate = db.Column(db.Date, nullable=True)
    calibrationprovider = db.Column(db.String(150), nullable=True)

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

    asset = db.relationship(
        'Asset',
        backref=db.backref('measuringtool', uselist=False, lazy='joined'),
    )
    measuringtooltype = db.relationship('MeasuringToolType', backref='tools')

    __table_args__ = (
        db.Index('idx_measuringtool_type', 'measuringtooltypeid'),
    )

    def __repr__(self):
        return f"<MeasuringTool {self.assetid}>"

    def calibrationstatus(self, today=None):
        return derive_status(self.nextcalibrationdate, today)

    def to_dict(self, today=None):
        """Extension dict with the type name and derived calibration status."""
        result = super().to_dict()
        # BaseModel.to_dict only isoformats datetime, not plain date columns.
        # Emit calibration dates as 'YYYY-MM-DD' so the frontend parses them
        # the same way it parses warranty dates.
        for field in ('lastcalibrationdate', 'nextcalibrationdate'):
            value = getattr(self, field)
            result[field] = value.isoformat() if value else None
        if self.measuringtooltype:
            result['measuringtooltypename'] = self.measuringtooltype.name
            result['measuringtooltypecolor'] = self.measuringtooltype.color
        status = self.calibrationstatus(today)
        result['calibrationstatus'] = status
        result['calibrationstatuscolor'] = STATUS_COLORS.get(
            status, STATUS_COLORS['unknown'])
        return result
