"""Sépare le super administrateur du rôle administrateur configurable.

La migration ne supprime aucune donnée. Les comptes existants reçoivent
explicitement ``super_admin`` afin de garantir la continuité d'accès.
"""
from alembic import op
import sqlalchemy as sa

revision = "9f4a1b2c3d5e"
down_revision = "9e39f0a1b2c3"
branch_labels = None
depends_on = None

PERMISSIONS = {
    "dashboard.view", "intervention.view", "intervention.create", "intervention.edit",
    "intervention.assign", "intervention.change_status", "intervention.close", "intervention.reject",
    "intervention.delete", "intervention.postpone", "intervention.comment", "patrimoine.view",
    "patrimoine.create", "patrimoine.edit", "patrimoine.move", "patrimoine.change_quantity",
    "patrimoine.retire", "patrimoine.delete", "planning.view", "planning.create", "planning.edit",
    "planning.delete", "planning.manage", "stock.view", "stock.edit", "stock.receive", "stock.issue",
    "stock.transfer", "stock.inventory", "stock.adjust", "stock.destroy", "stock.transvasement",
    "stock.dilute", "stock.configure", "contract.view", "contract.manage", "prevention.view",
    "prevention.manage", "export.use", "housing.private", "gmao_config.view", "gmao_config.configure",
    "watchdog_dnd.view", "watchdog_dnd.configure", "integration.ent.view", "integration.ent.configure",
    "integration.outlook.view", "integration.outlook.configure", "integration.pronote.view",
    "integration.pronote.configure", "integration.yeastar.view", "integration.yeastar.configure",
    "user.view", "user.create", "user.edit", "user.delete", "user.manage", "role.view", "role.create",
    "role.edit", "role.archive", "role.manage", "audit.view", "audit.export", "system.view",
    "system.configure", "system.admin",
}

LABELS = {
    "dashboard.view": ("Consulter le tableau de bord", "Permet d'accéder au tableau de bord général de la GMAO."),
    "intervention.view": ("Consulter les interventions", "Permet de consulter la liste et le détail des interventions."),
    "intervention.create": ("Créer une intervention", "Permet d'enregistrer une nouvelle intervention dans la GMAO."),
    "intervention.edit": ("Modifier une intervention", "Permet de modifier une intervention existante."),
    "intervention.assign": ("Affecter une intervention", "Permet d'affecter une intervention à un agent ou une entreprise."),
    "intervention.change_status": ("Changer le statut d'une intervention", "Permet de faire évoluer le statut d'une intervention selon son workflow."),
    "intervention.close": ("Clôturer une intervention", "Permet de clôturer une intervention terminée et d'enregistrer son état final."),
    "intervention.reject": ("Refuser une intervention", "Permet de refuser une demande en enregistrant un motif."),
    "patrimoine.view": ("Consulter le patrimoine", "Permet de consulter les bâtiments, zones, locaux et équipements."),
    "patrimoine.edit": ("Modifier le patrimoine", "Permet de modifier les informations des éléments du patrimoine."),
    "patrimoine.move": ("Déplacer des équipements", "Permet de déplacer des équipements entre les locaux."),
    "stock.view": ("Consulter les stocks", "Permet de consulter les produits, lots, quantités et emplacements."),
    "stock.receive": ("Réceptionner des produits", "Permet d'enregistrer l'entrée en stock de produits et leurs lots."),
    "stock.issue": ("Sortir des produits du stock", "Permet d'enregistrer une sortie de produits vers un agent ou pour utilisation."),
    "stock.transfer": ("Transférer du stock", "Permet de déplacer une quantité entre emplacements."),
    "stock.inventory": ("Réaliser un inventaire", "Permet d'enregistrer un comptage physique et de traiter les écarts."),
    "stock.dilute": ("Préparer une dilution", "Permet d'enregistrer la préparation d'un produit dilué."),
    "system.configure": ("Modifier la configuration générale", "Permet de modifier les paramètres généraux de l'application."),
    "user.manage": ("Gérer les utilisateurs", "Permet de créer, modifier, désactiver et administrer les comptes."),
    "role.manage": ("Gérer les rôles et permissions", "Permet de créer et modifier les rôles et leurs permissions."),
}


def _label(code):
    if code in LABELS:
        return LABELS[code]
    module, _, action = code.partition(".")
    return (f"{action.replace('_', ' ').capitalize()} — {module.replace('_', ' ').capitalize()}",
            f"Permet d'effectuer l'action « {action.replace('_', ' ')} » dans le domaine concerné.")


def upgrade():
    bind = op.get_bind()
    bind.execute(sa.text("""INSERT INTO roles (slug,name,description,is_system,is_active,created_at,updated_at)
        SELECT 'super_admin','Super administrateur','Rôle système protégé donnant un accès complet à l'application.',1,1,NOW(),NOW()
        FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM roles WHERE slug='super_admin')"""))
    bind.execute(sa.text("""UPDATE roles SET name='Super administrateur', description='Rôle système protégé donnant un accès complet à l'application.', is_system=1, is_active=1 WHERE slug='super_admin'"""))
    bind.execute(sa.text("""UPDATE roles SET name='Administrateur', description='Rôle système destiné à l’administration courante de la GMAO. Ses permissions sont configurables.' WHERE slug='admin'"""))
    role_text = {
        'responsable_gmao': ('Responsable GMAO', 'Rôle système destiné à la supervision et à la gestion générale de la GMAO.'),
        'technicien': ('Technicien', 'Rôle système destiné aux opérations de maintenance.'),
        'assistant_prevention': ('Assistant de prévention', 'Rôle système donnant accès aux fonctions liées à la prévention.'),
        'demandeur': ('Demandeur', 'Rôle système destiné aux utilisateurs pouvant créer et suivre des demandes.'),
        'lecture': ('Lecture seule', 'Rôle système destiné aux utilisateurs autorisés uniquement à consulter certaines informations.'),
    }
    for slug, (name, description) in role_text.items():
        bind.execute(sa.text("UPDATE roles SET name=:name, description=:description WHERE slug=:slug"), dict(slug=slug, name=name, description=description))
    bind.execute(sa.text("""INSERT INTO permissions (code,name,module,action,description,is_active)
        SELECT '*','Accès complet à l’application','system','all','Accès complet réservé au super administrateur.',1
        FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM permissions WHERE code='*')"""))
    bind.execute(sa.text("UPDATE permissions SET name='Accès complet à l’application', description='Accès complet réservé au super administrateur.', is_active=1 WHERE code='*'"))
    for code in sorted(PERMISSIONS):
        name, description = _label(code)
        module, _, action = code.partition('.')
        bind.execute(sa.text("""INSERT INTO permissions (code,name,module,action,description,is_active)
            SELECT :code,:name,:module,:action,:description,1 FROM DUAL
            WHERE NOT EXISTS (SELECT 1 FROM permissions WHERE code=:code)"""), dict(code=code, name=name, module=module, action=action, description=description))
        bind.execute(sa.text("UPDATE permissions SET name=:name, description=:description, module=:module, action=:action, is_active=1 WHERE code=:code"), dict(code=code, name=name, module=module, action=action, description=description))
    # Les permissions historiques conservées reçoivent elles aussi un
    # intitulé humain, sans modifier leur code utilisé par le runtime.
    for row in bind.execute(sa.text("SELECT code,module,action FROM permissions WHERE code <> '*'" )).all():
        name, description = _label(row.code)
        bind.execute(sa.text("UPDATE permissions SET name=:name, description=:description WHERE code=:code"), dict(code=row.code, name=name, description=description))
    # Le wildcard ne doit plus appartenir à admin.
    bind.execute(sa.text("DELETE rp FROM role_permissions rp JOIN roles r ON r.id=rp.role_id JOIN permissions p ON p.id=rp.permission_id WHERE r.slug='admin' AND p.code='*'"))
    bind.execute(sa.text("""INSERT INTO role_permissions (role_id,permission_id,effect)
        SELECT r.id,p.id,'allow' FROM roles r JOIN permissions p
        WHERE r.slug='super_admin' AND p.code='*'
        AND NOT EXISTS (SELECT 1 FROM role_permissions rp WHERE rp.role_id=r.id AND rp.permission_id=p.id)"""))
    bind.execute(sa.text("""DELETE rp FROM role_permissions rp JOIN roles r ON r.id=rp.role_id
        JOIN permissions p ON p.id=rp.permission_id
        WHERE r.slug='admin' AND p.code IN ('intervention.manage','patrimoine.manage','stock.manage')"""))
    # Admin reçoit les permissions applicatives explicites (dont les anciennes
    # permissions conservées en base), sans le wildcard.
    bind.execute(sa.text("""INSERT INTO role_permissions (role_id,permission_id,effect)
        SELECT r.id,p.id,'allow' FROM roles r CROSS JOIN permissions p
        WHERE r.slug='admin' AND p.code <> '*' AND p.is_active=1
        AND NOT EXISTS (SELECT 1 FROM role_permissions rp WHERE rp.role_id=r.id AND rp.permission_id=p.id)"""))
    # Continuité d'accès : tous les comptes existants reçoivent le rôle protégé.
    bind.execute(sa.text("""INSERT INTO user_roles (user_id,role_id,assigned_at)
        SELECT u.id,r.id,NOW() FROM users u CROSS JOIN roles r WHERE r.slug='super_admin'
        AND NOT EXISTS (SELECT 1 FROM user_roles ur WHERE ur.user_id=u.id AND ur.role_id=r.id)"""))


def downgrade():
    # Aucun lien utilisateur ni permission ne doit être supprimé par rollback.
    pass
