import { ref, reactive } from 'vue'
import { Notify } from 'quasar'
import { usePermissions } from './usePermissions'

export function useUserGroups ({ permGroups, loadGroupMembers: externalLoadGroupMembers } = {}) {
  const { HUB_URL } = usePermissions()

  // Users tab
  const userSearch = ref('')
  const userResults = ref([])
  const userSearchLoading = ref(false)
  const userSearchDone = ref(false)
  const selectedUser = ref(null)
  const savingGroups = ref(false)
  const savingName = ref(false)

  // Groups tab
  const selectedGroup = ref(null)
  const groupMembers = ref(null)
  const groupMembersLoading = ref(false)
  const addMemberSearch = ref('')
  const memberSearchResults = ref([])
  const memberSearchLoading = ref(false)

  let searchTimer = null
  function debouncedSearchUsers () {
    clearTimeout(searchTimer)
    searchTimer = setTimeout(() => searchUsers(), 300)
  }

  async function searchUsers () {
    const q = (userSearch.value || '').trim()
    userSearchLoading.value = true
    userSearchDone.value = false
    try {
      const url = q.length >= 1
        ? HUB_URL + '/auth/users?search=' + encodeURIComponent(q)
        : HUB_URL + '/auth/users?page=1'
      const res = await fetch(url)
      const data = await res.json()
      userResults.value = data.users.map(u => ({
        ...u,
        overrides: reactive({ ...u.overrides }),
      }))
      userSearchDone.value = true
    } catch (e) {
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
    } finally {
      userSearchLoading.value = false
    }
  }

  function selectUser (u) {
    selectedUser.value = selectedUser.value?.pk === u.pk ? null : u
  }

  async function toggleUserGroup (user, groupName) {
    const idx = user.groups.indexOf(groupName)
    if (idx >= 0) {
      user.groups.splice(idx, 1)
    } else {
      user.groups.push(groupName)
    }
    savingGroups.value = true
    try {
      await fetch(HUB_URL + '/auth/users/' + encodeURIComponent(user.email) + '/groups', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ groups: user.groups }),
      })
      Notify.create({ type: 'positive', message: `Groupes mis a jour pour ${user.name || user.email}`, timeout: 1500 })
    } catch (e) {
      if (idx >= 0) user.groups.push(groupName)
      else user.groups.splice(user.groups.indexOf(groupName), 1)
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
    } finally {
      savingGroups.value = false
    }
  }

  // Édite le NOM COMPLET Authentik (source unique affichée partout, y compris le
  // From sortant personnel). PUT /auth/users/{email}/name.
  async function saveUserName (user, newName) {
    const name = String(newName || '').trim()
    if (!name || name === user.name) return false
    savingName.value = true
    const previous = user.name
    try {
      const r = await fetch(HUB_URL + '/auth/users/' + encodeURIComponent(user.email) + '/name', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name }),
      })
      const data = await r.json()
      if (!r.ok) throw new Error(data?.error || ('HTTP ' + r.status))
      user.name = name
      // Reflète aussi dans la liste de résultats si l'utilisateur y figure.
      const inList = userResults.value.find(u => u.pk === user.pk)
      if (inList) inList.name = name
      Notify.create({ type: 'positive', message: `Nom mis à jour : ${name}`, timeout: 1500 })
      return true
    } catch (e) {
      user.name = previous
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
      return false
    } finally {
      savingName.value = false
    }
  }

  async function selectGroup (name) {
    if (selectedGroup.value === name) {
      selectedGroup.value = null
      groupMembers.value = null
      return
    }
    selectedGroup.value = name
    addMemberSearch.value = ''
    memberSearchResults.value = []
    await loadGroupMembers(name)
  }

  async function loadGroupMembers (name) {
    groupMembersLoading.value = true
    try {
      const res = await fetch(HUB_URL + '/auth/users?group=' + encodeURIComponent(name))
      const data = await res.json()
      groupMembers.value = data.users
    } catch (e) {
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
    } finally {
      groupMembersLoading.value = false
    }
  }

  async function removeFromGroup (member, groupName) {
    const newGroups = member.groups.filter(g => g !== groupName)
    try {
      await fetch(HUB_URL + '/auth/users/' + encodeURIComponent(member.email) + '/groups', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ groups: newGroups }),
      })
      groupMembers.value = groupMembers.value.filter(m => m.pk !== member.pk)
      if (permGroups) {
        const g = permGroups.value.find(g => g.name === groupName)
        if (g) g.num_users = Math.max(0, g.num_users - 1)
      }
      Notify.create({ type: 'positive', message: `${member.name || member.email} retire de ${groupName}`, timeout: 1500 })
    } catch (e) {
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
    }
  }

  let memberTimer = null
  function debouncedMemberSearch () {
    clearTimeout(memberTimer)
    const q = (addMemberSearch.value || '').trim()
    if (q.length < 2) { memberSearchResults.value = []; return }
    memberTimer = setTimeout(() => searchMembersToAdd(q), 300)
  }

  async function searchMembersToAdd (q) {
    memberSearchLoading.value = true
    try {
      const res = await fetch(HUB_URL + '/auth/users?search=' + encodeURIComponent(q))
      const data = await res.json()
      const existingPks = new Set((groupMembers.value || []).map(m => m.pk))
      memberSearchResults.value = data.users.filter(u => !existingPks.has(u.pk))
    } catch (e) {
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
    } finally {
      memberSearchLoading.value = false
    }
  }

  async function addUserToCurrentGroup (user) {
    if (!selectedGroup.value) return
    if (user.groups.includes(selectedGroup.value)) return
    memberSearchResults.value = []
    addMemberSearch.value = ''
    try {
      const newGroups = [...new Set([...user.groups, selectedGroup.value])]
      await fetch(HUB_URL + '/auth/users/' + encodeURIComponent(user.email) + '/groups', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ groups: newGroups }),
      })
      user.groups = newGroups
      groupMembers.value.push(user)
      if (permGroups) {
        const g = permGroups.value.find(g => g.name === selectedGroup.value)
        if (g) g.num_users++
      }
      Notify.create({ type: 'positive', message: `${user.name || user.email} ajoute a ${selectedGroup.value}`, timeout: 1500 })
    } catch (e) {
      Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
    }
  }

  // ── Invite flow ───────────────────────────────────────────────────────────
  // POST /auth/users on the hub creates a user in BOTH Authentik (with
  // recovery email) and ERPNext (System User, mapped to the Authentik
  // social login). We surface the dialog state here so the Settings page
  // can drive it without re-implementing the round-trip.
  const inviteOpen = ref(false)
  const inviteSaving = ref(false)
  const inviteForm = reactive({ email: '', full_name: '', groups: ['sysadmin'], roles: ['Employee'], rolesText: 'Employee' })
  // Holds the result of the last successful invite so the dialog can show
  // the recovery URL the admin needs to forward to the new user.
  const lastInviteResult = ref(null)

  function openInviteDialog (presetGroup) {
    inviteForm.email = ''
    inviteForm.full_name = ''
    inviteForm.groups = presetGroup ? [presetGroup] : ['sysadmin']
    inviteForm.roles = ['Employee']
    inviteForm.rolesText = 'Employee'
    lastInviteResult.value = null
    inviteOpen.value = true
  }

  async function submitInvite () {
    const email = inviteForm.email.trim().toLowerCase()
    const fullName = inviteForm.full_name.trim()
    if (!email || !fullName) {
      Notify.create({ type: 'warning', message: 'Email et nom requis' })
      return
    }
    inviteSaving.value = true
    try {
      const r = await fetch(HUB_URL + '/auth/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, full_name: fullName, groups: inviteForm.groups, roles: inviteForm.roles }),
      })
      const data = await r.json()
      if (!r.ok) throw new Error(data?.error || ('HTTP ' + r.status))
      // Optimistic insert into the visible list so the admin sees it
      // immediately. A subsequent search refresh will reconcile.
      userResults.value.unshift({ ...data.user, overrides: reactive({ ...(data.user.overrides || {}) }) })
      // Hub generates a temp password and emails it via Mailjet.
      // We surface BOTH the password (so admin can copy/relay) and
      // whether the email actually went out (so admin knows if they
      // need to relay it manually via Slack/SMS).
      lastInviteResult.value = {
        full_name: fullName,
        email,
        temp_password: data.temp_password || null,
        email_sent: !!data.email_sent,
        erpnext_ok: data.erpnext?.ok !== false,
        erpnext_error: data.erpnext?.error || null,
      }
      Notify.create({
        type: 'positive', timeout: 4500,
        message: data.email_sent
          ? `${fullName} ajouté — courriel envoyé à ${email}`
          : `${fullName} ajouté — copiez le mot de passe temporaire pour ${email}`,
      })
      // Don't auto-close so the admin can grab the recovery URL.
    } catch (e) {
      Notify.create({ type: 'negative', message: 'Erreur invitation: ' + e.message, timeout: 5000 })
    } finally {
      inviteSaving.value = false
    }
  }

  return {
    userSearch, userResults, userSearchLoading, userSearchDone,
    selectedUser, savingGroups, savingName,
    selectedGroup, groupMembers, groupMembersLoading,
    addMemberSearch, memberSearchResults, memberSearchLoading,
    debouncedSearchUsers, searchUsers, selectUser, toggleUserGroup, saveUserName,
    selectGroup, loadGroupMembers, removeFromGroup,
    debouncedMemberSearch, searchMembersToAdd, addUserToCurrentGroup,
    inviteOpen, inviteSaving, inviteForm, lastInviteResult, openInviteDialog, submitInvite,
  }
}
