from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Enum, Integer, Float, String, Date, Column, Boolean
from config import item_attributes, sql_conf
from definitions.attributes import *
from werkzeug.security import generate_password_hash, check_password_hash

# Initialize SQLAlchemy
db = SQLAlchemy()

# Users table
class User(db.Model):
    """User model for authentication."""
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password_hash = db.Column(db.String(256), nullable=False)  # Increase length to 256

    def set_password(self, password: str) -> None:
        """Hash the password and store it."""
        self.password_hash = generate_password_hash(password)

    def check_password(self, password: str) -> bool:
        """Check if the provided password matches the stored hash."""
        return check_password_hash(self.password_hash, password)

# Mapping of attribute types to SQLAlchemy column types
COLUMN_TYPE_MAPPING = {
    textAttribute: lambda attrib: String(attrib.max_length),  # Map text attributes to String columns
    dateAttribute: lambda attrib: Date,  # Map date attributes to Date columns
    selectAttribute: lambda attrib: Enum(*attrib.options),  # Map select attributes to Enum columns
    intAttribute: lambda attrib: Integer,  # Map integer attributes to Integer columns
    floatAttribute: lambda attrib: Float,  # Map float attributes to Float columns
}

def _get_column_type(attrib):
    """Helper function to get the SQLAlchemy column type for an attribute."""
    if type(attrib) not in COLUMN_TYPE_MAPPING:
        raise ValueError(
            f"Unsupported attribute type: {type(attrib)} for attribute '{attrib.attrib_name}'. "
            f"Supported types are: {list(COLUMN_TYPE_MAPPING.keys())}"
        )
    return COLUMN_TYPE_MAPPING[type(attrib)](attrib)

def _get_column_properties(attrib):
    """Helper function to get the properties for a column."""
    return {
        'primary_key': attrib.primary,
        'unique': attrib.unique,
        'nullable': not attrib.required,
        'default': attrib.default_val
    }

def create_asset_model():
    """
    Dynamically creates the Asset model based on the configuration in item_attributes.
    """
    # Define the table name and basic methods
    attrs = {
        '__tablename__': sql_conf.SQL_TABLE,
        '__init__': lambda self, **kwargs: self.__dict__.update(kwargs),  # Allow dynamic assignment of attributes
        '__repr__': lambda self: f"<Asset {getattr(self, next(attrib.attrib_name for attrib in item_attributes if attrib.primary))}>"  # Provide a readable representation of the object
    }

    # Define columns based on item_attributes
    for attrib in item_attributes:
        column_type = _get_column_type(attrib)
        column_properties = _get_column_properties(attrib)
        attrs[attrib.attrib_name] = Column(column_type, **column_properties)

    # Create the Item class dynamically
    Item = type('Item', (db.Model,), attrs)
    return Item

# Create the Item model
Asset = create_asset_model()