"""Measurement fact table - counter-based measurements from meters."""

from datetime import datetime

from sqlalchemy import (
    BigInteger,
    Column,
    DateTime,
    Float,
    ForeignKey,
    Index,
    Integer,
    SmallInteger,
    UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import UUID

from iot_db.models.base import Base


class FactMeasurement(Base):
    """Fact table for counter-based measurements (electricity, water, heat, gas).

    This table stores cumulative counter readings from various meters across all
    IoT sources. Counter values are monotonically increasing over time.

    Used for:
    - Electricity meters (kWh)
    - Water meters (m3)
    - Heat meters (kWh)
    - Gas meters (m3)

    The table is optimized for time-series queries and supports partitioning
    by measured_ts for efficient data management.

    Example records:

    Electricity meter reading:
        tenant: <uuid>
        device_id: 123
        measured_ts: 2026-01-10 12:00:00
        value: 12345.67  # kWh
        quality: 100
        flags: 0

    Water meter reading:
        tenant: <uuid>
        device_id: 456
        measured_ts: 2026-01-10 12:00:00
        value: 234.56  # m3
        quality: 95
        flags: 0

    Attributes:
        id: Primary key (BigInteger for high volume)
        tenant: Tenant UUID (multi-tenant support)
        device_id: FK to dim_device
        measured_ts: Timestamp of measurement (partitioning key)
        date_id: Optional FK to dim_date for date-based aggregations
        value: Counter value (cumulative, e.g., total kWh, m3)
        quality: Quality score (0-100, optional)
        flags: Bit flags for data quality indicators:
            - 0x01: estimated value
            - 0x02: interpolated
            - 0x04: suspicious/anomaly
            - 0x08: manually entered
        created_ts: Record creation timestamp

    Table configuration:
        - Partitioned by measured_ts (monthly partitions recommended)
        - Unique constraint: (tenant, device_id, measured_ts)
        - Indexes optimized for time-series queries
    """

    __tablename__ = "fact_measurement"

    # Primary key
    id = Column(BigInteger, primary_key=True)

    # Dimension foreign keys
    tenant = Column(UUID(as_uuid=True), nullable=False, index=True)
    device_id = Column(
        Integer,
        ForeignKey("dim_device.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
    )

    # Time dimension (partitioning key)
    measured_ts = Column(DateTime, nullable=False, index=True)
    date_id = Column(
        Integer,
        ForeignKey("dim_date.date_id", ondelete="SET NULL"),
        nullable=True,
    )  # Optional link to dim_date

    # Measurement value
    value = Column(Float, nullable=False)  # Counter value (kWh, m3, etc.)

    # Data quality
    quality = Column(SmallInteger)  # 0-100 quality score
    flags = Column(Integer, default=0)  # Bit flags: estimated, interpolated, etc.

    # Timestamps
    created_ts = Column(DateTime, default=datetime.utcnow, nullable=False)

    __table_args__ = (
        # Unique constraint: one measurement per device per timestamp
        UniqueConstraint(
            "tenant", "device_id", "measured_ts", name="uq_measurement_device_ts"
        ),
        # Composite indexes for common query patterns
        Index("idx_measurement_tenant_ts", "tenant", "measured_ts"),
        Index("idx_measurement_device_ts", "device_id", "measured_ts"),
        Index("idx_measurement_date", "date_id"),
        # Partitioning configuration (requires manual partition creation)
        # {'postgresql_partition_by': 'RANGE (measured_ts)'},
    )

    def __repr__(self):
        return (
            f"<FactMeasurement(id={self.id}, device_id={self.device_id}, "
            f"measured_ts={self.measured_ts}, value={self.value})>"
        )

    # Flag bit masks
    FLAG_ESTIMATED = 0x01
    FLAG_INTERPOLATED = 0x02
    FLAG_SUSPICIOUS = 0x04
    FLAG_MANUAL = 0x08

    def is_estimated(self) -> bool:
        """Check if measurement is estimated."""
        return bool(self.flags & self.FLAG_ESTIMATED)

    def is_interpolated(self) -> bool:
        """Check if measurement is interpolated."""
        return bool(self.flags & self.FLAG_INTERPOLATED)

    def is_suspicious(self) -> bool:
        """Check if measurement is marked as suspicious."""
        return bool(self.flags & self.FLAG_SUSPICIOUS)

    def is_manual(self) -> bool:
        """Check if measurement was manually entered."""
        return bool(self.flags & self.FLAG_MANUAL)

    def set_flag(self, flag: int):
        """Set a quality flag."""
        self.flags = (self.flags or 0) | flag

    def clear_flag(self, flag: int):
        """Clear a quality flag."""
        self.flags = (self.flags or 0) & ~flag
