import { render, screen } from '@testing-library/react'
import React from 'react'
import { ScopedBlock } from './ScopedBlock'
import { MissingScopeAction } from './models'

import * as zocksSession from '../../store/session/hooks'

const setupMockZocksAuth = (hasScope: boolean) => {
  jest.spyOn(zocksSession, 'useZocksAuth').mockReturnValue({ hasScope: jest.fn().mockReturnValue(hasScope) })
}

afterEach(() => jest.restoreAllMocks())

test('Scoped Block does not render anything if action is hide and there is no scope', async () => {
  setupMockZocksAuth(false)
  render(
    <ScopedBlock missingScopeAction={MissingScopeAction.HIDE} scope={'room:list'} superScope={['room:admin']}>
      <div>
        <button>List rooms</button>
      </div>
    </ScopedBlock>
  )
  expect(screen.queryByRole('button', { name: 'List rooms' })).not.toBeInTheDocument()
})

test('Scoped Block disables buttons in children and grand children elements too if action is disabled and there is no scope', async () => {
  setupMockZocksAuth(false)
  render(
    <ScopedBlock missingScopeAction={MissingScopeAction.DISABLE} scope={'room:list'} superScope={['room:admin']}>
      <div>
        <button>List rooms grandchildren</button>
      </div>
      <button>List rooms children</button>
    </ScopedBlock>
  )
  expect(screen.getByRole('button', { name: 'List rooms grandchildren' })).toBeDisabled()
  expect(screen.getByRole('button', { name: 'List rooms children' })).toBeDisabled()
})

test('Scoped Block does not do anything if action is ignore and there is no scope', async () => {
  setupMockZocksAuth(false)
  render(
    <ScopedBlock missingScopeAction={MissingScopeAction.IGNORE} scope={'room:list'} superScope={['room:admin']}>
      <div>
        <button>List rooms grandchildren</button>
      </div>
      <button>List rooms children</button>
    </ScopedBlock>
  )
  expect(screen.getByRole('button', { name: 'List rooms grandchildren' })).toBeEnabled()
  expect(screen.getByRole('button', { name: 'List rooms children' })).toBeEnabled()
})

test('Scoped Block does not do anything if action is hide and there is scope', async () => {
  setupMockZocksAuth(true)
  render(
    <ScopedBlock missingScopeAction={MissingScopeAction.HIDE} scope={'room:list'} superScope={['room:admin']}>
      <div>
        <button>List rooms grandchildren</button>
      </div>
      <button>List rooms children</button>
    </ScopedBlock>
  )
  expect(screen.getByRole('button', { name: 'List rooms grandchildren' })).toBeEnabled()
  expect(screen.getByRole('button', { name: 'List rooms children' })).toBeEnabled()
})

test('Scoped Block does handle text without tag', async () => {
  setupMockZocksAuth(true)
  render(
    <ScopedBlock missingScopeAction={MissingScopeAction.DISABLE} scope={'room:list'} superScope={['room:admin']}>
      Text
    </ScopedBlock>
  )
  expect(screen.getByText('Text')).toBeInTheDocument()
})

test('Scoped Block does not fail if empty', async () => {
  setupMockZocksAuth(true)
  const { container } = render(
    <ScopedBlock missingScopeAction={MissingScopeAction.DISABLE} scope={'room:list'} superScope={['room:admin']} />
  )
  expect(container).toBeEmptyDOMElement()
})

test('Scoped Block button will be disabled even if inner Scoped Block should allow it', async () => {
  jest
    .spyOn(zocksSession, 'useZocksAuth')
    .mockImplementationOnce(() => ({ hasScope: jest.fn().mockReturnValue(false) }))
    .mockImplementationOnce(() => ({ hasScope: jest.fn().mockReturnValue(true) }))
  render(
    <ScopedBlock missingScopeAction={MissingScopeAction.DISABLE} scope={'room:list'} superScope={['room:admin']}>
      <ScopedBlock missingScopeAction={MissingScopeAction.DISABLE} scope={'room:list2'} superScope={['room:admin']}>
        <button>List rooms grandchildren</button>
      </ScopedBlock>
      <button>List rooms children</button>
    </ScopedBlock>
  )
  expect(screen.getByRole('button', { name: 'List rooms grandchildren' })).toBeDisabled()
  expect(screen.getByRole('button', { name: 'List rooms children' })).toBeDisabled()
})

test('Scoped Block considers custom auth actions', async () => {
  const mockFalseAuthActions = { hasScope: jest.fn().mockReturnValue(false) }
  const mockTrueAuthActions = { hasScope: jest.fn().mockReturnValue(true) }

  setupMockZocksAuth(true)
  render(
    <ScopedBlock
      authActions={mockFalseAuthActions}
      missingScopeAction={MissingScopeAction.HIDE}
      scope={'room:list'}
      superScope={['room:admin']}
    >
      <div>
        <button>List rooms</button>
      </div>
    </ScopedBlock>
  )
  expect(screen.queryByRole('button', { name: 'List rooms' })).not.toBeInTheDocument()

  setupMockZocksAuth(false)
  render(
    <ScopedBlock
      authActions={mockTrueAuthActions}
      missingScopeAction={MissingScopeAction.HIDE}
      scope={'room:list'}
      superScope={['room:admin']}
    >
      <div>
        <button>List rooms</button>
      </div>
    </ScopedBlock>
  )
  expect(screen.getByRole('button', { name: 'List rooms' })).toBeInTheDocument()
})
