import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'

/**
 * Query key factory for workspace-related queries.
 * Provides hierarchical cache keys for workspaces, settings, and permissions.
 */
type WorkspaceQueryScope = 'active' | 'archived' | 'all'

export const workspaceKeys = {
  all: ['workspace'] as const,
  lists: () => [...workspaceKeys.all, 'list'] as const,
  list: (scope: WorkspaceQueryScope = 'active') =>
    [...workspaceKeys.lists(), 'user', scope] as const,
  details: () => [...workspaceKeys.all, 'detail'] as const,
  detail: (id: string) => [...workspaceKeys.details(), id] as const,
  settings: (id: string) => [...workspaceKeys.detail(id), 'settings'] as const,
  permissions: (id: string) => [...workspaceKeys.detail(id), 'permissions'] as const,
  members: (id: string) => [...workspaceKeys.detail(id), 'members'] as const,
  adminLists: () => [...workspaceKeys.all, 'adminList'] as const,
  adminList: (userId: string | undefined) => [...workspaceKeys.adminLists(), userId ?? ''] as const,
}

/** Represents a workspace in the user's workspace list. */
export interface Workspace {
  id: string
  name: string
  color?: string
  ownerId: string
  role?: string
  membershipId?: string
  permissions?: 'admin' | 'write' | 'read' | null
}

async function fetchWorkspaces(
  scope: WorkspaceQueryScope = 'active',
  signal?: AbortSignal
): Promise<Workspace[]> {
  const response = await fetch(`/api/workspaces?scope=${scope}`, { signal })

  if (!response.ok) {
    throw new Error('Failed to fetch workspaces')
  }

  const data = await response.json()
  return data.workspaces || []
}

/**
 * Fetches the current user's workspaces.
 * @param enabled - Whether the query should execute (defaults to true)
 */
export function useWorkspacesQuery(enabled = true, scope: WorkspaceQueryScope = 'active') {
  return useQuery({
    queryKey: workspaceKeys.list(scope),
    queryFn: ({ signal }) => fetchWorkspaces(scope, signal),
    enabled,
    staleTime: 30 * 1000,
    placeholderData: keepPreviousData,
  })
}

interface CreateWorkspaceParams {
  name: string
}

/**
 * Creates a new workspace.
 * Merges the created row into the active list cache before invalidation so navigation
 * cannot race a stale list (see workspace validation fallback in use-workspace-management).
 */
export function useCreateWorkspace() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: async ({ name }: CreateWorkspaceParams) => {
      const response = await fetch('/api/workspaces', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name }),
      })

      if (!response.ok) {
        const error = await response.json()
        throw new Error(error.error || 'Failed to create workspace')
      }

      const data = await response.json()
      return data.workspace as Workspace
    },
    onSuccess: (newWorkspace) => {
      queryClient.setQueryData<Workspace[]>(workspaceKeys.list('active'), (previous) => {
        if (!previous?.length) {
          return [newWorkspace]
        }
        if (previous.some((w) => w.id === newWorkspace.id)) {
          return previous
        }
        return [newWorkspace, ...previous]
      })
      queryClient.invalidateQueries({ queryKey: workspaceKeys.lists() })
      queryClient.invalidateQueries({ queryKey: workspaceKeys.adminLists() })
    },
  })
}

interface DeleteWorkspaceParams {
  workspaceId: string
  deleteTemplates?: boolean
}

/**
 * Deletes a workspace.
 * Automatically invalidates the workspace list cache on success.
 */
export function useDeleteWorkspace() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: async ({ workspaceId, deleteTemplates = false }: DeleteWorkspaceParams) => {
      const response = await fetch(`/api/workspaces/${workspaceId}`, {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ deleteTemplates }),
      })

      if (!response.ok) {
        const error = await response.json()
        throw new Error(error.error || 'Failed to delete workspace')
      }

      return response.json()
    },
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: workspaceKeys.lists() })
      queryClient.invalidateQueries({ queryKey: workspaceKeys.detail(variables.workspaceId) })
    },
  })
}

interface UpdateWorkspaceParams {
  workspaceId: string
  name?: string
  color?: string
}

/**
 * Updates a workspace's properties (name, color, etc.).
 * Invalidates both the workspace list and the specific workspace detail cache.
 */
export function useUpdateWorkspace() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: async ({ workspaceId, ...updates }: UpdateWorkspaceParams) => {
      const body = updates.name !== undefined ? { ...updates, name: updates.name.trim() } : updates
      const response = await fetch(`/api/workspaces/${workspaceId}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      })

      if (!response.ok) {
        const error = await response.json()
        throw new Error(error.error || 'Failed to update workspace')
      }

      return response.json()
    },
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({ queryKey: workspaceKeys.lists() })
      queryClient.invalidateQueries({ queryKey: workspaceKeys.detail(variables.workspaceId) })
    },
  })
}

/** Represents a user with permissions in a workspace. */
export interface WorkspaceUser {
  userId: string
  email: string
  name: string | null
  image: string | null
  permissionType: 'admin' | 'write' | 'read'
}

/** Workspace permissions data containing all users and their access levels. */
export interface WorkspacePermissions {
  users: WorkspaceUser[]
  total: number
}

async function fetchWorkspacePermissions(
  workspaceId: string,
  signal?: AbortSignal
): Promise<WorkspacePermissions> {
  const response = await fetch(`/api/workspaces/${workspaceId}/permissions`, { signal })

  if (!response.ok) {
    if (response.status === 404) {
      throw new Error('Workspace not found or access denied')
    }
    if (response.status === 401) {
      throw new Error('Authentication required')
    }
    throw new Error(`Failed to fetch permissions: ${response.statusText}`)
  }

  return response.json()
}

/**
 * Fetches permissions for a specific workspace.
 * @param workspaceId - The workspace ID to fetch permissions for
 */
export function useWorkspacePermissionsQuery(workspaceId: string | null | undefined) {
  return useQuery({
    queryKey: workspaceKeys.permissions(workspaceId ?? ''),
    queryFn: ({ signal }) => fetchWorkspacePermissions(workspaceId as string, signal),
    enabled: Boolean(workspaceId),
    staleTime: 30 * 1000,
    placeholderData: keepPreviousData,
  })
}

/** Lightweight member profile for UI display (avatars, owner cells). */
export interface WorkspaceMember {
  userId: string
  name: string
  image: string | null
}

async function fetchWorkspaceMembers(
  workspaceId: string,
  signal?: AbortSignal
): Promise<WorkspaceMember[]> {
  const response = await fetch(`/api/workspaces/${workspaceId}/members`, { signal })

  if (!response.ok) {
    throw new Error('Failed to fetch workspace members')
  }

  const data = await response.json()
  return data.members || []
}

/**
 * Fetches lightweight member profiles (id, name, image) for a workspace.
 * Use this for display purposes (avatars, owner cells) instead of the heavier permissions query.
 */
export function useWorkspaceMembersQuery(workspaceId: string | null | undefined) {
  return useQuery({
    queryKey: workspaceKeys.members(workspaceId ?? ''),
    queryFn: ({ signal }) => fetchWorkspaceMembers(workspaceId as string, signal),
    enabled: Boolean(workspaceId),
    staleTime: 5 * 60 * 1000,
  })
}

async function fetchWorkspaceSettings(workspaceId: string, signal?: AbortSignal) {
  const [settingsResponse, permissionsResponse] = await Promise.all([
    fetch(`/api/workspaces/${workspaceId}`, { signal }),
    fetch(`/api/workspaces/${workspaceId}/permissions`, { signal }),
  ])

  if (!settingsResponse.ok || !permissionsResponse.ok) {
    throw new Error('Failed to fetch workspace settings')
  }

  const [settings, permissions] = await Promise.all([
    settingsResponse.json(),
    permissionsResponse.json(),
  ])

  return {
    settings,
    permissions,
  }
}

/**
 * Fetches workspace settings including permissions.
 * @param workspaceId - The workspace ID to fetch settings for
 */
export function useWorkspaceSettings(workspaceId: string) {
  return useQuery({
    queryKey: workspaceKeys.settings(workspaceId),
    queryFn: ({ signal }) => fetchWorkspaceSettings(workspaceId, signal),
    enabled: !!workspaceId,
    staleTime: 30 * 1000,
    placeholderData: keepPreviousData,
  })
}

interface UpdateWorkspaceSettingsParams {
  workspaceId: string
  billedAccountUserId?: string
  billingAccountUserEmail?: string
}

/**
 * Updates workspace settings (e.g., billing configuration).
 * Invalidates the workspace settings cache on success.
 */
export function useUpdateWorkspaceSettings() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: async ({ workspaceId, ...updates }: UpdateWorkspaceSettingsParams) => {
      const response = await fetch(`/api/workspaces/${workspaceId}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(updates),
      })

      if (!response.ok) {
        const error = await response.json()
        throw new Error(error.message || 'Failed to update workspace settings')
      }

      return response.json()
    },
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({
        queryKey: workspaceKeys.settings(variables.workspaceId),
      })
    },
  })
}

/** Workspace with admin access metadata. */
export interface AdminWorkspace {
  id: string
  name: string
  isOwner: boolean
  ownerId?: string
  canInvite: boolean
}

async function fetchAdminWorkspaces(
  userId: string | undefined,
  signal?: AbortSignal
): Promise<AdminWorkspace[]> {
  if (!userId) {
    return []
  }

  const workspacesResponse = await fetch('/api/workspaces', { signal })
  if (!workspacesResponse.ok) {
    throw new Error('Failed to fetch workspaces')
  }

  const workspacesData = await workspacesResponse.json()
  const allUserWorkspaces = workspacesData.workspaces || []

  const permissionPromises = allUserWorkspaces.map(
    async (workspace: { id: string; name: string; isOwner?: boolean; ownerId?: string }) => {
      try {
        const permissionResponse = await fetch(`/api/workspaces/${workspace.id}/permissions`, {
          signal,
        })
        if (!permissionResponse.ok) {
          return null
        }
        const permissionData = await permissionResponse.json()
        return { workspace, permissionData }
      } catch (_error) {
        return null
      }
    }
  )

  const results = await Promise.all(permissionPromises)

  const adminWorkspaces: AdminWorkspace[] = []
  for (const result of results) {
    if (!result) continue

    const { workspace, permissionData } = result
    let hasAdminAccess = false

    if (permissionData.users) {
      const currentUserPermission = permissionData.users.find(
        (user: { id: string; userId?: string; permissionType: string }) =>
          user.id === userId || user.userId === userId
      )
      hasAdminAccess = currentUserPermission?.permissionType === 'admin'
    }

    const isOwner = workspace.isOwner || workspace.ownerId === userId

    if (hasAdminAccess || isOwner) {
      adminWorkspaces.push({
        id: workspace.id,
        name: workspace.name,
        isOwner,
        ownerId: workspace.ownerId,
        canInvite: true,
      })
    }
  }

  return adminWorkspaces
}

/**
 * Fetches workspaces where the user has admin access.
 * @param userId - The user ID to check admin access for
 */
export function useAdminWorkspaces(userId: string | undefined) {
  return useQuery({
    queryKey: workspaceKeys.adminList(userId),
    queryFn: ({ signal }) => fetchAdminWorkspaces(userId, signal),
    enabled: Boolean(userId),
    staleTime: 60 * 1000,
    placeholderData: keepPreviousData,
  })
}
