"""
Map legacy group_ad to ERPNext Role Profiles and assign roles to users.

Legacy groups:
  admin       → Full admin access (System Manager + all modules)
  sysadmin    → Technical admin (System Manager, HR, all operations)
  tech        → Field technicians (Dispatch, limited Accounts read)
  support     → Customer support (Support Team, Sales read, Dispatch)
  comptabilite → Accounting (Accounts Manager, HR User)
  facturation  → Billing (Accounts User, Sales User)

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/setup_user_roles.py
"""
import frappe
import pymysql
import os
import time
import hashlib
from datetime import datetime

os.chdir("/home/frappe/frappe-bench/sites")
frappe.init(site="erp.gigafibre.ca", sites_path=".")
frappe.connect()
frappe.local.flags.ignore_permissions = True
print("Connected:", frappe.local.site)

now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")

# ═══════════════════════════════════════════════════════════════
# ROLE PROFILE DEFINITIONS
# ═══════════════════════════════════════════════════════════════

# Roles each group should have
ROLE_MAP = {
    "admin": [
        "System Manager", "Accounts Manager", "Accounts User",
        "Sales Manager", "Sales User", "HR Manager", "HR User",
        "Support Team", "Dispatch Technician", "Employee",
        "Projects Manager", "Stock Manager", "Stock User",
        "Purchase Manager", "Purchase User", "Website Manager",
        "Report Manager", "Dashboard Manager",
    ],
    "sysadmin": [
        "System Manager", "Accounts User",
        "Sales Manager", "Sales User", "HR User",
        "Support Team", "Dispatch Technician", "Employee",
        "Projects Manager", "Stock Manager", "Stock User",
        "Purchase User", "Website Manager",
        "Report Manager", "Dashboard Manager",
    ],
    "tech": [
        "Dispatch Technician", "Employee",
        "Support Team", "Stock User",
    ],
    "support": [
        "Support Team", "Employee",
        "Sales User", "Dispatch Technician",
        "Accounts User",
    ],
    "comptabilite": [
        "Accounts Manager", "Accounts User", "Employee",
        "HR User", "Sales User", "Report Manager",
    ],
    "facturation": [
        "Accounts User", "Employee",
        "Sales User", "Report Manager",
    ],
}

# Profile display names
PROFILE_NAMES = {
    "admin": "Admin - Full Access",
    "sysadmin": "SysAdmin - Technical",
    "tech": "Technician - Field",
    "support": "Support - Customer Service",
    "comptabilite": "Comptabilité - Accounting",
    "facturation": "Facturation - Billing",
}

# ═══════════════════════════════════════════════════════════════
# PHASE 1: Create/update Role Profiles
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 1: CREATE ROLE PROFILES")
print("="*60)

for group_key, roles in ROLE_MAP.items():
    profile_name = PROFILE_NAMES[group_key]

    # Delete existing profile and its roles
    frappe.db.sql('DELETE FROM "tabHas Role" WHERE parent = %s AND parenttype = %s',
                  (profile_name, "Role Profile"))
    if frappe.db.exists("Role Profile", profile_name):
        frappe.db.sql('DELETE FROM "tabRole Profile" WHERE name = %s', (profile_name,))

    # Create profile
    frappe.db.sql("""
        INSERT INTO "tabRole Profile" (name, creation, modified, modified_by, owner, docstatus, idx, role_profile)
        VALUES (%(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, 0, %(name)s)
    """, {"name": profile_name, "now": now_str})

    # Add roles
    for i, role in enumerate(roles):
        rname = "rp-{}-{}-{}".format(group_key, i, int(time.time()))
        frappe.db.sql("""
            INSERT INTO "tabHas Role" (
                name, creation, modified, modified_by, owner, docstatus, idx,
                parent, parentfield, parenttype, role
            ) VALUES (
                %(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, %(idx)s,
                %(parent)s, 'roles', 'Role Profile', %(role)s
            )
        """, {"name": rname, "now": now_str, "idx": i + 1, "parent": profile_name, "role": role})

    frappe.db.commit()
    print("  Created profile '{}' with {} roles: {}".format(
        profile_name, len(roles), ", ".join(roles[:5]) + ("..." if len(roles) > 5 else "")))

# ═══════════════════════════════════════════════════════════════
# PHASE 2: Get employee → user → group mapping
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 2: MAP EMPLOYEES TO ROLES")
print("="*60)

conn = pymysql.connect(
    host="legacy-db",
    user="facturation",
    password="*******",
    database="gestionclient",
    cursorclass=pymysql.cursors.DictCursor
)

with conn.cursor() as cur:
    cur.execute("""
        SELECT id, username, first_name, last_name, email, group_ad, status
        FROM staff WHERE status = 1 AND email IS NOT NULL AND email != ''
    """)
    active_staff = cur.fetchall()

conn.close()

# Build email → group_ad map
email_to_group = {}
for s in active_staff:
    email = s["email"].strip().lower()
    if email:
        email_to_group[email] = (s["group_ad"] or "").strip().lower()

print("Active staff with email: {}".format(len(email_to_group)))

# Get all ERPNext users
erp_users = frappe.db.sql("""
    SELECT name FROM "tabUser"
    WHERE name NOT IN ('Administrator', 'Guest', 'admin@example.com')
      AND enabled = 1
""", as_dict=True)

print("ERPNext users: {}".format(len(erp_users)))

# ═══════════════════════════════════════════════════════════════
# PHASE 3: Assign roles to users
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 3: ASSIGN ROLES")
print("="*60)

assigned = 0
skipped = 0

for user in erp_users:
    email = user["name"].lower()
    group = email_to_group.get(email)

    if not group or group not in ROLE_MAP:
        skipped += 1
        continue

    profile_name = PROFILE_NAMES[group]
    target_roles = set(ROLE_MAP[group])
    # Always add "All" and "Desk User" which are standard
    target_roles.add("All")
    target_roles.add("Desk User")

    # Get current roles
    current_roles = set()
    current = frappe.db.sql("""
        SELECT role FROM "tabHas Role"
        WHERE parent = %s AND parenttype = 'User'
    """, (user["name"],))
    for r in current:
        current_roles.add(r[0])

    # Remove roles not in target (except a few we should keep)
    keep_always = {"All", "Desk User"}
    to_remove = current_roles - target_roles - keep_always
    to_add = target_roles - current_roles

    if to_remove:
        for role in to_remove:
            frappe.db.sql("""
                DELETE FROM "tabHas Role"
                WHERE parent = %s AND parenttype = 'User' AND role = %s
            """, (user["name"], role))

    if to_add:
        for role in to_add:
            rname = "ur-{}-{}".format(hashlib.md5("{}{}".format(user["name"], role).encode()).hexdigest()[:10], int(time.time()))
            frappe.db.sql("""
                INSERT INTO "tabHas Role" (
                    name, creation, modified, modified_by, owner, docstatus, idx,
                    parent, parentfield, parenttype, role
                ) VALUES (
                    %(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, 0,
                    %(parent)s, 'roles', 'User', %(role)s
                )
            """, {"name": rname, "now": now_str, "parent": user["name"], "role": role})

    # Set role profile on user
    frappe.db.sql("""
        UPDATE "tabUser" SET role_profile_name = %s WHERE name = %s
    """, (profile_name, user["name"]))

    assigned += 1
    if to_add or to_remove:
        print("  {} → {} (+{} -{})".format(
            user["name"], profile_name, len(to_add), len(to_remove)))

frappe.db.commit()
print("\nAssigned roles to {} users ({} skipped - no legacy group match)".format(assigned, skipped))

# Also link Employee → User
print("\n--- Linking Employee → User ---")
linked = 0
employees = frappe.db.sql("""
    SELECT name, company_email FROM "tabEmployee"
    WHERE status = 'Active' AND company_email IS NOT NULL
""", as_dict=True)

for emp in employees:
    email = emp["company_email"]
    # Check if user exists
    user_exists = frappe.db.exists("User", email)
    if user_exists:
        frappe.db.sql("""
            UPDATE "tabEmployee" SET user_id = %s WHERE name = %s
        """, (email, emp["name"]))
        linked += 1

frappe.db.commit()
print("Linked {} employees to their User accounts".format(linked))

# ═══════════════════════════════════════════════════════════════
# PHASE 4: VERIFY
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 4: VERIFY")
print("="*60)

# Count users per role profile
by_profile = frappe.db.sql("""
    SELECT role_profile_name, COUNT(*) as cnt FROM "tabUser"
    WHERE role_profile_name IS NOT NULL AND role_profile_name != ''
    GROUP BY role_profile_name ORDER BY cnt DESC
""", as_dict=True)
print("Users by Role Profile:")
for p in by_profile:
    print("  {}: {}".format(p["role_profile_name"], p["cnt"]))

# Sample users with their roles
sample_users = frappe.db.sql("""
    SELECT u.name, u.full_name, u.role_profile_name,
           STRING_AGG(hr.role, ', ' ORDER BY hr.role) as roles
    FROM "tabUser" u
    LEFT JOIN "tabHas Role" hr ON hr.parent = u.name AND hr.parenttype = 'User'
    WHERE u.role_profile_name IS NOT NULL AND u.role_profile_name != ''
    GROUP BY u.name, u.full_name, u.role_profile_name
    ORDER BY u.role_profile_name, u.name
""", as_dict=True)
print("\nUsers and their roles:")
for u in sample_users:
    print("  {} ({}) → {} roles: {}".format(
        u["name"], u["full_name"] or "", u["role_profile_name"],
        u["roles"][:80] if u["roles"] else "(none)"))

frappe.clear_cache()
print("\nDone — cache cleared")
