"""
app/models.py – Modelos SQLAlchemy.
Representan exactamente las tablas definidas en 01_schema.sql.
"""
from datetime import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from app import db, login_manager


# ---------------------------------------------------------------
# Tabla: roles
# ---------------------------------------------------------------
class Rol(db.Model):
    __tablename__ = 'roles'

    id     = db.Column(db.Integer, primary_key=True)
    nombre = db.Column(db.String(50), unique=True, nullable=False)

    usuarios = db.relationship('Usuario', back_populates='rol', lazy='dynamic')

    def __repr__(self):
        return f'<Rol {self.nombre}>'


# ---------------------------------------------------------------
# Tabla: usuarios
# ---------------------------------------------------------------
class Usuario(UserMixin, db.Model):
    __tablename__ = 'usuarios'

    id              = db.Column(db.Integer, primary_key=True)
    nombre_completo = db.Column(db.String(150), nullable=False)
    correo          = db.Column(db.String(150), unique=True, nullable=False)
    password_hash   = db.Column(db.String(255), nullable=False)
    puesto          = db.Column(db.String(100), nullable=False)
    rol_id          = db.Column(db.Integer, db.ForeignKey('roles.id'), nullable=False)
    activo          = db.Column(db.Boolean, default=True, nullable=False)
    creado_en       = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)

    rol     = db.relationship('Rol', back_populates='usuarios')
    sistemas = db.relationship('UsuarioSistema', back_populates='usuario', lazy='dynamic')

    # Cambios donde el usuario participa
    solicitudes  = db.relationship('Cambio', foreign_keys='Cambio.usuario_solicitante_id',
                                   back_populates='solicitante', lazy='dynamic')
    desarrollos  = db.relationship('Cambio', foreign_keys='Cambio.desarrollador_id',
                                   back_populates='desarrollador', lazy='dynamic')
    validaciones = db.relationship('Cambio', foreign_keys='Cambio.usuario_valido_id',
                                   back_populates='usuario_valido', lazy='dynamic')
    liberaciones = db.relationship('Cambio', foreign_keys='Cambio.administrador_libero_id',
                                   back_populates='admin_libero', lazy='dynamic')

    def set_password(self, password: str):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password: str) -> bool:
        return check_password_hash(self.password_hash, password)

    @property
    def es_admin(self) -> bool:
        return self.rol.nombre == 'Administrador'

    @property
    def es_desarrollador(self) -> bool:
        return self.rol.nombre == 'Desarrollador'

    @property
    def es_promotor(self) -> bool:
        return self.rol.nombre == 'Promotor'

    def get_id(self):
        return str(self.id)

    def __repr__(self):
        return f'<Usuario {self.correo}>'


@login_manager.user_loader
def load_user(user_id: str):
    return Usuario.query.get(int(user_id))


# ---------------------------------------------------------------
# Tabla: sistemas
# ---------------------------------------------------------------
class Sistema(db.Model):
    __tablename__ = 'sistemas'

    id          = db.Column(db.Integer, primary_key=True)
    nombre      = db.Column(db.String(150), unique=True, nullable=False)
    descripcion = db.Column(db.Text)
    activo      = db.Column(db.Boolean, default=True, nullable=False)

    usuarios = db.relationship('UsuarioSistema', back_populates='sistema', lazy='dynamic')
    cambios  = db.relationship('Cambio', back_populates='sistema', lazy='dynamic')

    def __repr__(self):
        return f'<Sistema {self.nombre}>'


# ---------------------------------------------------------------
# Tabla: usuario_sistema
# ---------------------------------------------------------------
class UsuarioSistema(db.Model):
    __tablename__ = 'usuario_sistema'

    id         = db.Column(db.Integer, primary_key=True)
    usuario_id = db.Column(db.Integer, db.ForeignKey('usuarios.id'), nullable=False)
    sistema_id = db.Column(db.Integer, db.ForeignKey('sistemas.id'), nullable=False)

    __table_args__ = (
        db.UniqueConstraint('usuario_id', 'sistema_id', name='uq_usuario_sistema'),
    )

    usuario = db.relationship('Usuario', back_populates='sistemas')
    sistema = db.relationship('Sistema', back_populates='usuarios')

    def __repr__(self):
        return f'<UsuarioSistema u={self.usuario_id} s={self.sistema_id}>'


# ---------------------------------------------------------------
# Tabla: cambios
# ---------------------------------------------------------------
class Cambio(db.Model):
    __tablename__ = 'cambios'

    id                         = db.Column(db.Integer, primary_key=True)
    sistema_id                 = db.Column(db.Integer, db.ForeignKey('sistemas.id'), nullable=False)
    usuario_solicitante_id     = db.Column(db.Integer, db.ForeignKey('usuarios.id'), nullable=False)
    numero_incidencia          = db.Column(db.Integer, nullable=False)
    descripcion_breve          = db.Column(db.Text, nullable=False)
    urgente                    = db.Column(db.Boolean, nullable=False, default=False)
    fecha_pruebas              = db.Column(db.Date, nullable=False)

    # Transacción 2
    descripcion_tecnica        = db.Column(db.Text)
    fecha_propuesta_produccion = db.Column(db.Date)
    desarrollador_id           = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    archivo_nombre             = db.Column(db.String(255))
    archivo_cambio             = db.Column(db.LargeBinary)

    # Transacción 3
    fecha_real_aplicacion      = db.Column(db.Date)
    usuario_valido_id          = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    estado_cambio              = db.Column(db.Enum('Cambio Correcto', 'Cambio Incorrecto'))
    observaciones              = db.Column(db.Text)
    administrador_libero_id    = db.Column(db.Integer, db.ForeignKey('usuarios.id'))

    # Control
    estatus     = db.Column(
        db.Enum('Solicitado', 'Programado', 'Liberado'),
        nullable=False, default='Solicitado'
    )
    creado_en      = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    actualizado_en = db.Column(db.DateTime, onupdate=datetime.utcnow)

    # Relaciones
    sistema     = db.relationship('Sistema',  foreign_keys=[sistema_id],             back_populates='cambios')
    solicitante = db.relationship('Usuario',  foreign_keys=[usuario_solicitante_id], back_populates='solicitudes')
    desarrollador = db.relationship('Usuario',foreign_keys=[desarrollador_id],       back_populates='desarrollos')
    usuario_valido = db.relationship('Usuario',foreign_keys=[usuario_valido_id],     back_populates='validaciones')
    admin_libero   = db.relationship('Usuario',foreign_keys=[administrador_libero_id],back_populates='liberaciones')

    def __repr__(self):
        return f'<Cambio #{self.id} [{self.estatus}]>'
