import enum
import uuid
from datetime import datetime
from typing import Optional

from pydantic import BaseModel, Field

from extensions import db


# ============================================
# Machine Meta Schema
# ============================================

class MachineMetaSchema(BaseModel):
    manufacturer: Optional[str] = None
    model_number: Optional[str] = None
    serial_number: Optional[str] = None
    location: Optional[str] = None
    install_date: Optional[str] = None
    rated_capacity: Optional[float] = None
    tags: list[str] = Field(default_factory=list)
    notes: Optional[str] = None


# ============================================
# Machines
# ============================================

class Machine(db.Model):

    __tablename__ = "machines"

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

    # unique internal name
    name = db.Column(
        db.String(128),
        nullable=False,
        unique=True
    )

    # human readable name
    display_name = db.Column(
        db.String(128),
        nullable=False
    )

    # machine category/type
    machine_type = db.Column(
        db.String(100),
        nullable=False
    )

    # running / stopped / maintenance / alarm
    status = db.Column(
        db.String(50),
        nullable=False,
        default="stopped"
    )

    # physical location in plant
    location = db.Column(
        db.String(200),
        nullable=True
    )

    # extended structured metadata
    meta = db.Column(
        db.JSON,
        nullable=True
    )

    created_at = db.Column(
        db.DateTime,
        default=datetime.utcnow
    )

    parameters = db.relationship(
        "Parameter",
        back_populates="machine",
        cascade="all, delete-orphan"
    )

# ============================================
# Parameter Domain
# ============================================

class ParameterDomain(enum.Enum):
    PRODUCTION = "production"
    MAINTENANCE = "maintenance"
    QUALITY = "quality"


# ============================================
# Parameters
# ============================================

class Parameter(db.Model):

    __tablename__ = "parameters"

    id = db.Column(
        db.String(36),
        primary_key=True,
        default=lambda: str(uuid.uuid4())
    )

    machine_id = db.Column(
        db.Integer,
        db.ForeignKey("machines.id"),
        nullable=False
    )

    name = db.Column(
        db.String(200),
        nullable=False
    )

    tag = db.Column(
        db.String(200),
        nullable=False
    )

    display_name = db.Column(
        db.String(200),
        nullable=False
    )

    data_type = db.Column(
        db.String(50),
        nullable=False
    )

    unit = db.Column(
        db.String(50),
        nullable=True
    )

    domain = db.Column(
        db.Enum(ParameterDomain),
        nullable=False
    )

    min_value = db.Column(
        db.Float,
        nullable=True
    )

    max_value = db.Column(
        db.Float,
        nullable=True
    )

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

    created_at = db.Column(
        db.DateTime,
        default=datetime.utcnow
    )

    machine = db.relationship(
        "Machine",
        back_populates="parameters"
    )

    parameter_values = db.relationship(
        "ParameterValue",
        backref="parameter",
        cascade="all, delete-orphan",
        lazy=True
    )

    __table_args__ = (
        db.UniqueConstraint(
            "machine_id",
            "name",
            name="uq_machine_parameter_name"
        ),
        db.UniqueConstraint(
            "machine_id",
            "tag",
            name="uq_machine_parameter_tag"
        ),
    )

# ============================================
# Parameter Values
# ============================================

class ParameterValue(db.Model):

    __tablename__ = "parameter_values"

    id = db.Column(
        db.String(36),
        primary_key=True,
        default=lambda: str(uuid.uuid4())
    )

    parameter_id = db.Column(
        db.String(36),
        db.ForeignKey("parameters.id"),
        nullable=False
    )

    value = db.Column(
        db.Float,
        nullable=False
    )

    timestamp = db.Column(
        db.DateTime,
        default=datetime.utcnow
    )