"""Usage fact table - aggregated consumption data for meters."""

from datetime import datetime

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

from iot_db.models.base import Base


class FactUsage(Base):
    """Fact table for aggregated consumption/usage data from meters.

    This table stores pre-calculated consumption values for various time periods
    (hourly, daily, monthly). It's derived from FactMeasurement by calculating
    the difference between counter readings.

    Used for:
    - Daily/monthly electricity consumption reports
    - Water usage statistics
    - Heat consumption tracking
    - Cost calculations per period

    The table enables fast reporting and analytics without needing to recalculate
    consumption from raw measurements.

    Example records:

    Daily electricity usage:
        tenant: <uuid>
        device_id: 123
        date_id: 20260110
        period_type: "daily"
        start_ts: 2026-01-10 00:00:00
        end_ts: 2026-01-10 23:59:59
        start_value: 12345.67
        end_value: 12370.89
        consumption: 25.22  # kWh
        price_per_unit: 0.65
        total_cost: 16.39
        currency: "PLN"
        quality_score: 100

    Monthly water usage:
        tenant: <uuid>
        device_id: 456
        date_id: 20260100  # First day of month
        period_type: "monthly"
        start_ts: 2026-01-01 00:00:00
        end_ts: 2026-01-31 23:59:59
        start_value: 234.56
        end_value: 241.23
        consumption: 6.67  # m3
        price_per_unit: 4.50
        total_cost: 30.02
        currency: "PLN"
        quality_score: 98

    Hourly electricity usage:
        tenant: <uuid>
        device_id: 789
        date_id: 20260110
        period_type: "hourly"
        start_ts: 2026-01-10 12:00:00
        end_ts: 2026-01-10 12:59:59
        start_value: 12345.67
        end_value: 12346.89
        consumption: 1.22  # kWh
        price_per_unit: 0.65
        total_cost: 0.79
        currency: "PLN"
        quality_score: 100

    Attributes:
        id: Primary key (BigInteger)
        tenant: Tenant UUID (multi-tenant support)
        device_id: FK to dim_device
        date_id: FK to dim_date (date of period start)
        period_type: Period granularity ("hourly", "daily", "monthly", "yearly")
        start_ts: Period start timestamp
        end_ts: Period end timestamp
        start_value: Counter value at period start
        end_value: Counter value at period end
        consumption: Calculated consumption (end_value - start_value)
        price_per_unit: Price per unit for this period
        total_cost: Total cost (consumption * price_per_unit)
        currency: Currency code (PLN, EUR, USD, etc.)
        calculation_ts: When consumption was calculated
        quality_score: Data completeness score (0-100%)
        created_ts: Record creation timestamp

    Table configuration:
        - Unique constraint: (tenant, device_id, period_type, start_ts)
        - Indexes optimized for period-based queries
        - Quality score indicates data completeness (100% = no gaps)
    """

    __tablename__ = "fact_usage"

    # 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,
    )
    date_id = Column(
        Integer, ForeignKey("dim_date.date_id", ondelete="SET NULL"), nullable=True
    )

    # Period definition
    period_type = Column(
        Text, nullable=False, index=True
    )  # "hourly", "daily", "monthly", "yearly"
    start_ts = Column(DateTime, nullable=False, index=True)
    end_ts = Column(DateTime, nullable=False)

    # Counter values
    start_value = Column(Float)  # Counter value at start
    end_value = Column(Float)  # Counter value at end
    consumption = Column(Float, nullable=False)  # end_value - start_value

    # Cost calculation (optional)
    price_per_unit = Column(Float)  # Price per unit (per kWh, m3, etc.)
    total_cost = Column(Float)  # consumption * price_per_unit
    currency = Column(Text)  # PLN, EUR, USD, etc.

    # Metadata
    calculation_ts = Column(DateTime)  # When this record was calculated
    quality_score = Column(SmallInteger)  # 0-100 - data completeness percentage

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

    __table_args__ = (
        # Unique constraint: one usage record per device per period
        UniqueConstraint(
            "tenant",
            "device_id",
            "period_type",
            "start_ts",
            name="uq_usage_device_period",
        ),
        # Composite indexes for common query patterns
        Index("idx_usage_tenant_period", "tenant", "period_type", "start_ts"),
        Index("idx_usage_device_period", "device_id", "start_ts"),
        Index("idx_usage_date", "date_id"),
        # Index for cost queries
        Index(
            "idx_usage_cost",
            "tenant",
            "start_ts",
            postgresql_where=(Column("total_cost").isnot(None)),
        ),
    )

    def __repr__(self):
        return (
            f"<FactUsage(id={self.id}, device_id={self.device_id}, "
            f"period_type={self.period_type}, start_ts={self.start_ts}, "
            f"consumption={self.consumption})>"
        )

    # Period type constants
    PERIOD_HOURLY = "hourly"
    PERIOD_DAILY = "daily"
    PERIOD_MONTHLY = "monthly"
    PERIOD_YEARLY = "yearly"

    def calculate_cost(self) -> float | None:
        """Calculate total cost from consumption and price.

        Returns:
            Total cost or None if price not available
        """
        if self.consumption is not None and self.price_per_unit is not None:
            return self.consumption * self.price_per_unit
        return None

    def set_cost(self, price_per_unit: float, currency: str = "PLN"):
        """Set price and calculate total cost.

        Args:
            price_per_unit: Price per unit of consumption
            currency: Currency code (default: PLN)
        """
        self.price_per_unit = price_per_unit
        self.currency = currency
        self.total_cost = self.calculate_cost()

    def is_complete(self, threshold: int = 95) -> bool:
        """Check if usage data is complete enough.

        Args:
            threshold: Minimum quality score (default: 95%)

        Returns:
            True if quality_score >= threshold
        """
        return self.quality_score is not None and self.quality_score >= threshold
