import { useCallback, useState } from "react"
import { api } from "@/lib/api"
import { ENDPOINTS } from "@/constants/endpoints"
import { createEntityHook } from "./useEntityBase"
import type {
  AdminRoleRead,
  AdminUserRead,
  CreateUserResponse,
  IpBlacklistCreatePayload,
  IpBlacklistRead,
  IpBlacklistUpdatePayload,
  PendingApprovalUser,
  RegeneratePasswordResponse,
  UserCreatePayload,
  UserUpdatePayload,
  UserWhitelistIpRead,
} from "@/types"

export const useUsersBase = createEntityHook<AdminUserRead, UserCreatePayload, UserUpdatePayload>(
  "Users",
  ENDPOINTS.USERS.LIST,
)

export const useIpBlacklistsBase = createEntityHook<
  IpBlacklistRead,
  IpBlacklistCreatePayload,
  IpBlacklistUpdatePayload
>("IP blacklist", ENDPOINTS.IP_BLACKLISTS.LIST)

function unwrap<T>(response: unknown, fallback: T): T {
  const value = response as { data?: unknown }
  if (value && typeof value === "object" && "data" in value) {
    return (value.data ?? fallback) as T
  }
  return (response ?? fallback) as T
}

function getErrorMessage(err: unknown, fallback: string) {
  if (err instanceof Error) return err.message || fallback
  return fallback
}

export function useUsers() {
  const usersBase = useUsersBase()
  const blacklistsBase = useIpBlacklistsBase()

  const [pendingApprovals, setPendingApprovals] = useState<PendingApprovalUser[]>([])
  const [roles, setRoles] = useState<AdminRoleRead[]>([])
  const [selectedUserRoles, setSelectedUserRoles] = useState<AdminRoleRead[]>([])
  const [whitelistIps, setWhitelistIps] = useState<string[]>([])
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [actionError, setActionError] = useState<string | null>(null)

  const refreshUsers = useCallback(async (search = "") => {
    await usersBase.fetchItems(1, 100, search)
  }, [usersBase.fetchItems])

  const fetchPendingApprovals = useCallback(async () => {
    try {
      const response = await api.get(ENDPOINTS.USERS.PENDING_APPROVALS)
      setPendingApprovals(unwrap<PendingApprovalUser[]>(response, []))
    } catch {
      setPendingApprovals([])
    }
  }, [])

  const fetchRoles = useCallback(async () => {
    try {
      const response = await api.get(ENDPOINTS.ROLES.LIST, {
        params: { skip: 0, limit: 100 },
      })
      setRoles(unwrap<AdminRoleRead[]>(response, []))
    } catch {
      setRoles([])
    }
  }, [])

  const fetchUserDetail = useCallback(async (userId: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const [userResponse, rolesResponse] = await Promise.all([
        api.get(ENDPOINTS.USERS.GET_BY_ID(userId)),
        api.get(ENDPOINTS.ROLES.USER_ROLES(userId)).catch(() => null),
      ])
      const user = unwrap<AdminUserRead | null>(userResponse, null)
      const userRoles = unwrap<AdminRoleRead[]>(rolesResponse, [])
      usersBase.setSelectedItem(user)
      setSelectedUserRoles(userRoles)
      return { user, roles: userRoles }
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to fetch user details."))
      usersBase.setSelectedItem(null)
      setSelectedUserRoles([])
      return null
    } finally {
      setIsSubmitting(false)
    }
  }, [usersBase.setSelectedItem])

  const createUser = useCallback(async (payload: UserCreatePayload) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const response = await api.post(ENDPOINTS.USERS.CREATE, payload)
      await Promise.all([refreshUsers(), fetchPendingApprovals()])
      return unwrap<CreateUserResponse | null>(response, null)
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to create user."))
      return null
    } finally {
      setIsSubmitting(false)
    }
  }, [fetchPendingApprovals, refreshUsers])

  const updateUser = useCallback(async (userId: string, payload: UserUpdatePayload, roleIds?: string[]) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const updated = await usersBase.updateItem(userId, payload)
      if (!updated) return false
      if (roleIds) {
        await api.put(ENDPOINTS.ROLES.USER_ROLES(userId), { role_ids: roleIds })
      }
      await fetchUserDetail(userId)
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to update user."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [fetchUserDetail, usersBase.updateItem])

  const approveAdmin = useCallback(async (userId: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      await api.post(ENDPOINTS.USERS.APPROVE_ADMIN(userId))
      await Promise.all([refreshUsers(), fetchPendingApprovals()])
      if (usersBase.selectedItem?.id === userId) await fetchUserDetail(userId)
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to approve admin access."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [fetchPendingApprovals, fetchUserDetail, refreshUsers, usersBase.selectedItem?.id])

  const regeneratePassword = useCallback(async (userId: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const response = await api.post(ENDPOINTS.USERS.REGENERATE_PASSWORD(userId))
      await refreshUsers()
      return unwrap<RegeneratePasswordResponse | null>(response, null)
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to regenerate password."))
      return null
    } finally {
      setIsSubmitting(false)
    }
  }, [refreshUsers])

  const revokeSessions = useCallback(async (userId: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      await api.post(ENDPOINTS.USERS.REVOKE_SESSIONS(userId))
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to revoke user sessions."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [])

  const deleteUser = useCallback(async (userId: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const deleted = await usersBase.deleteItem(userId)
      if (!deleted) return false
      if (usersBase.selectedItem?.id === userId) {
        usersBase.setSelectedItem(null)
        setSelectedUserRoles([])
      }
      await fetchPendingApprovals()
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to deactivate user."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [fetchPendingApprovals, usersBase.deleteItem, usersBase.selectedItem?.id, usersBase.setSelectedItem])

  const fetchWhitelistIps = useCallback(async (userId: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const response = await api.get(ENDPOINTS.USERS.WHITELIST_IPS(userId))
      const data = unwrap<UserWhitelistIpRead | null>(response, null)
      const ips = data?.ip_addresses ?? []
      setWhitelistIps(ips)
      return ips
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to fetch IP allowlist."))
      setWhitelistIps([])
      return []
    } finally {
      setIsSubmitting(false)
    }
  }, [])

  const replaceWhitelistIps = useCallback(async (userId: string, ipAddresses: string[]) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const response = await api.put(ENDPOINTS.USERS.WHITELIST_IPS(userId), {
        ip_addresses: ipAddresses,
      })
      const data = unwrap<UserWhitelistIpRead | null>(response, null)
      setWhitelistIps(data?.ip_addresses ?? [])
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to update IP allowlist."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [])

  const fetchBlacklistEntries = useCallback(async (active?: boolean) => {
    await blacklistsBase.fetchItems(
      1,
      100,
      "",
      active === undefined ? undefined : { active },
    )
  }, [blacklistsBase.fetchItems])

  const createBlacklistEntry = useCallback(async (payload: IpBlacklistCreatePayload) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const created = await blacklistsBase.createItem(payload)
      return created
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to block IP address."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [blacklistsBase.createItem])

  const expireBlacklistEntry = useCallback(async (entryId: string, reason?: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      await api.post(ENDPOINTS.IP_BLACKLISTS.EXPIRE(entryId), { reason: reason || null })
      await fetchBlacklistEntries()
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to unblock IP address."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [fetchBlacklistEntries])

  return {
    users: usersBase.items,
    pendingApprovals,
    roles,
    selectedUser: usersBase.selectedItem,
    selectedUserRoles,
    whitelistIps,
    blacklistEntries: blacklistsBase.items,
    isLoading: usersBase.isLoading || blacklistsBase.isLoading,
    isSubmitting,
    error: actionError || usersBase.error || blacklistsBase.error,
    fetchUsers: refreshUsers,
    fetchPendingApprovals,
    fetchRoles,
    fetchUserDetail,
    createUser,
    updateUser,
    approveAdmin,
    regeneratePassword,
    revokeSessions,
    deleteUser,
    fetchWhitelistIps,
    replaceWhitelistIps,
    fetchBlacklistEntries,
    createBlacklistEntry,
    expireBlacklistEntry,
    setSelectedUser: usersBase.setSelectedItem,
    setWhitelistIps,
  }
}
