import '@testing-library/jest-dom'

import { fireEvent, render, screen } from '@testing-library/react'
import { vi } from 'vitest'

//mock element width to control mobile/desktop behavior
let mockedWidth = 1280

vi.mock('../../hooks/useElementWidth', async () => {
  return {
    useElementWidth: () => mockedWidth,
  }
})

import { PktHeaderService } from './HeaderService'

describe('PktHeaderService', () => {
  // jsdom does not implement scrollTo; our scroll lock hook uses it
  beforeAll(() => {
    window.scrollTo = vi.fn()
  })

  beforeEach(() => {
    mockedWidth = 1280
    vi.clearAllMocks()
  })

  it('renders service name and link when provided', () => {
    const { container } = render(<PktHeaderService serviceName="My Service" serviceLink="#" />)
    const host = container.querySelector('.pkt-header-service__service-link') as HTMLElement | null
    expect(host).toBeTruthy()
    expect(screen.getByText('My Service')).toBeInTheDocument()
  })

  it('applies compact class when compact=true', () => {
    const { container } = render(<PktHeaderService serviceName="Svc" compact />)
    const header = container.querySelector('.pkt-header-service')
    expect(header).toHaveClass('pkt-header-service--compact')
  })

  it('shows children inline on desktop, menu button on mobile', () => {
    // Desktop
    mockedWidth = 1440
    const { rerender } = render(
      <PktHeaderService serviceName="Svc">
        <a className="pkt-header-service__slot-link" href="#">
          Link
        </a>
      </PktHeaderService>,
    )
    expect(screen.getByText('Link')).toBeInTheDocument()

    // Mobile
    mockedWidth = 500
    rerender(
      <PktHeaderService serviceName="Svc">
        <a className="pkt-header-service__slot-link" href="#">
          Link
        </a>
      </PktHeaderService>,
    )
    expect(screen.getByRole('button', { name: 'Åpne meny' })).toBeInTheDocument()
  })

  it('renders desktop search input when showSearch is true (desktop)', () => {
    mockedWidth = 1400
    render(<PktHeaderService serviceName="Svc" showSearch={true} />)
    const search = screen.getByRole('searchbox', { name: 'Søk' })
    expect(search).toBeInTheDocument()
    expect(search).toHaveAttribute('id', 'header-service-search')
  })

  it('renders mobile search toggle and opens search field when clicked (mobile)', () => {
    mockedWidth = 500
    render(<PktHeaderService serviceName="Svc" showSearch={true} />)
    const btn = screen.getByRole('button', { name: 'Åpne søkefelt' })
    fireEvent.click(btn)
    // After opening, the mobile menu should show the search input
    expect(screen.getByRole('searchbox', { name: 'Søk' })).toBeInTheDocument()
    expect(screen.getByRole('searchbox')).toHaveAttribute('id', 'mobile-search-menu')
  })

  it('renders user button on desktop and opens user menu on click', () => {
    mockedWidth = 1400
    render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} />)
    // Button shows both fullname and shortname (both 'Aksel' when no shortname provided)
    const userBtn = screen.getByRole('button', { name: /Aksel/ })
    expect(userBtn).toBeInTheDocument()
    fireEvent.click(userBtn)
    // User menu should be mounted
    const menu = screen.getByRole('navigation')
    expect(menu).toHaveClass('pkt-user-menu')
  })

  // New tests for hideLogo prop
  describe('hideLogo prop', () => {
    it('shows oslo logo by default (hideLogo=false)', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" />)
      const logos = container.querySelectorAll('.pkt-header-service__logo')
      expect(logos.length).toBe(1)
    })

    it('hides oslo logo when hideLogo=true', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" hideLogo={true} />)
      const logos = container.querySelectorAll('.pkt-header-service__logo')
      expect(logos.length).toBe(0)
    })
  })

  // New tests for logoLink prop
  describe('logoLink prop', () => {
    it('renders logo as link when logoLink is a string', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" logoLink="https://www.oslo.kommune.no" />)
      const logoLink = container.querySelector('.pkt-header-service__logo a')
      expect(logoLink).toBeTruthy()
      expect(logoLink).toHaveAttribute('href', 'https://www.oslo.kommune.no')
    })

    it('lenker logoen til www.oslo.kommune.no som standard når logoLink ikke er oppgitt', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" />)
      const logoLink = container.querySelector('.pkt-header-service__logo a')
      expect(logoLink).toHaveAttribute('href', 'https://www.oslo.kommune.no')
    })
  })

  // New tests for position and scrollBehavior props
  describe('position and scrollBehavior props', () => {
    it('applies fixed class by default (position="fixed")', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" />)
      const header = container.querySelector('.pkt-header-service')
      expect(header).toHaveClass('pkt-header-service--fixed')
    })

    it('does not apply fixed class when position="relative"', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" position="relative" />)
      const header = container.querySelector('.pkt-header-service')
      expect(header).not.toHaveClass('pkt-header-service--fixed')
    })

    it('applies sticky class when position="sticky", without wrapper/spacer', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" position="sticky" />)
      const header = container.querySelector('.pkt-header-service')
      expect(header).toHaveClass('pkt-header-service--sticky')
      expect(header).not.toHaveClass('pkt-header-service--fixed')
      // Sticky stays in the document flow — no fixed-position wrapper or spacer.
      expect(container.querySelector('.pkt-header-service-wrapper')).toBeNull()
      expect(container.querySelector('.pkt-header-service-spacer')).toBeNull()
    })

    it('applies scroll-to-hide class by default (scrollBehavior="hide")', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" />)
      const header = container.querySelector('.pkt-header-service')
      expect(header).toHaveClass('pkt-header-service--scroll-to-hide')
    })

    it('does not apply scroll-to-hide class when scrollBehavior="none"', () => {
      const { container } = render(<PktHeaderService serviceName="Svc" scrollBehavior="none" />)
      const header = container.querySelector('.pkt-header-service')
      expect(header).not.toHaveClass('pkt-header-service--scroll-to-hide')
    })
  })

  // New tests for user with lastLoggedIn
  describe('user props', () => {
    it('displays user name in button', () => {
      mockedWidth = 1400
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel Olsen' }} />)
      const userBtn = screen.getByRole('button', { name: /Aksel Olsen/ })
      expect(userBtn).toBeInTheDocument()
    })

    it('logs deprecation warning when shortname is provided', () => {
      const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
      mockedWidth = 1400
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel', shortname: 'AO' }} />)
      expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('shortname'))
      consoleSpy.mockRestore()
    })

    it('displays formatted lastLoggedIn in user menu', () => {
      mockedWidth = 1400
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel', lastLoggedIn: new Date('2025-09-19') }} />)
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      // Should format the date in Norwegian
      expect(screen.getByText(/Sist pålogget:/)).toBeInTheDocument()
    })

    it('displays lastLoggedIn string as-is in user menu', () => {
      mockedWidth = 1400
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel', lastLoggedIn: '19. september 2025' }} />)
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      expect(screen.getByText('19. september 2025')).toBeInTheDocument()
    })
  })

  // New tests for representing props
  describe('representing props', () => {
    it('displays representing name in user button when representing is provided', () => {
      mockedWidth = 1400
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} representing={{ name: 'Oslo Kommune' }} />)
      const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
      expect(userBtn).toBeInTheDocument()
    })

    it('logs deprecation warning when representing.shortname is provided', () => {
      const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
      mockedWidth = 1400
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          representing={{ name: 'Oslo Kommune', shortname: 'OK' }}
        />,
      )
      expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('shortname'))
      consoleSpy.mockRestore()
    })

    it('displays orgNumber in user menu when representing has orgNumber', () => {
      mockedWidth = 1400
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          representing={{ name: 'Oslo Kommune', orgNumber: '911738066' }}
        />,
      )
      const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
      fireEvent.click(userBtn)
      expect(screen.getByText('Org.nr. 911738066')).toBeInTheDocument()
    })
  })

  // New tests for canChangeRepresentation
  describe('canChangeRepresentation prop', () => {
    it('shows change representation button when canChangeRepresentation=true', () => {
      mockedWidth = 1400
      const handleChange = vi.fn()
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          representing={{ name: 'Oslo Kommune' }}
          canChangeRepresentation={true}
          changeRepresentation={handleChange}
        />,
      )
      const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
      fireEvent.click(userBtn)
      const changeBtn = screen.getByRole('button', { name: 'Endre organisasjon' })
      expect(changeBtn).toBeInTheDocument()
      fireEvent.click(changeBtn)
      expect(handleChange).toHaveBeenCalledTimes(1)
    })

    it('does not show change representation button when canChangeRepresentation=false', () => {
      mockedWidth = 1400
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          representing={{ name: 'Oslo Kommune' }}
          canChangeRepresentation={false}
        />,
      )
      const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
      fireEvent.click(userBtn)
      expect(screen.queryByRole('button', { name: 'Endre organisasjon' })).not.toBeInTheDocument()
    })
  })

  // New tests for userMenu prop
  describe('userMenu prop', () => {
    it('renders userMenu items in user menu dropdown', () => {
      mockedWidth = 1400
      const userMenu = [
        { title: 'Mine bookinger', iconName: 'heart', target: '/bookinger' },
        { title: 'Innstillinger', iconName: 'cogwheel', target: () => {} },
      ]
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} userMenu={userMenu} />)
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      expect(screen.getByText('Mine bookinger')).toBeInTheDocument()
      expect(screen.getByText('Innstillinger')).toBeInTheDocument()
    })

    it('renders userMenu link items as links', () => {
      mockedWidth = 1400
      const userMenu = [{ title: 'Min profil', target: '/profil' }]
      const { container } = render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} userMenu={userMenu} />)
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      // PktLink renders as an <a> tag with class pkt-link
      const linkElement = container.querySelector('a.pkt-link[href="/profil"]')
      expect(linkElement).toBeTruthy()
      expect(screen.getByText('Min profil')).toBeInTheDocument()
    })

    it('calls userMenu button item onClick when clicked', () => {
      mockedWidth = 1400
      const handleClick = vi.fn()
      const userMenu = [{ title: 'Custom Action', target: handleClick }]
      render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} userMenu={userMenu} />)
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      const actionBtn = screen.getByRole('button', { name: 'Custom Action' })
      fireEvent.click(actionBtn)
      expect(handleClick).toHaveBeenCalledTimes(1)
    })
  })

  // New tests for logOut callback and placement
  describe('logOut callback', () => {
    it('shows logout button in header when logOutButtonPlacement="header"', () => {
      mockedWidth = 1400
      const handleLogout = vi.fn()
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          logOutButtonPlacement="header"
          logOut={handleLogout}
        />,
      )
      const logoutBtn = screen.getByRole('button', { name: 'Logg ut' })
      expect(logoutBtn).toBeInTheDocument()
      fireEvent.click(logoutBtn)
      expect(handleLogout).toHaveBeenCalledTimes(1)
    })

    it('shows logout button in userMenu when logOutButtonPlacement="userMenu"', () => {
      mockedWidth = 1400
      const handleLogout = vi.fn()
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          logOutButtonPlacement="userMenu"
          logOut={handleLogout}
        />,
      )
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      const logoutBtn = screen.getByRole('button', { name: 'Logg ut' })
      expect(logoutBtn).toBeInTheDocument()
      fireEvent.click(logoutBtn)
      expect(handleLogout).toHaveBeenCalledTimes(1)
    })

    it('shows logout button in both header and userMenu when logOutButtonPlacement="both"', () => {
      mockedWidth = 1400
      const handleLogout = vi.fn()
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          logOutButtonPlacement="both"
          logOut={handleLogout}
        />,
      )
      // Header logout
      const headerLogoutBtn = screen.getByRole('button', { name: 'Logg ut' })
      expect(headerLogoutBtn).toBeInTheDocument()

      // Open user menu
      const userBtn = screen.getByRole('button', { name: /Aksel/ })
      fireEvent.click(userBtn)
      // Should have logout in menu too
      const logoutBtns = screen.getAllByRole('button', { name: 'Logg ut' })
      expect(logoutBtns.length).toBe(2)
    })
  })

  // Test for userMenuFooter deprecation warning
  describe('userMenuFooter deprecation', () => {
    it('logs deprecation warning when userMenuFooter is provided', () => {
      const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
      mockedWidth = 1400
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          userMenuFooter={[{ title: 'Footer Item', target: '#' }]}
        />,
      )
      expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('userMenuFooter'))
      consoleSpy.mockRestore()
    })
  })

  // Test for userOptions "no longer available" warning
  describe('userOptions warning', () => {
    it('logs warning when userOptions is provided', () => {
      const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
      mockedWidth = 1400
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          userOptions={[{ title: 'Option', target: '#' }]}
        />,
      )
      expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('userOptions'))
      expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('no longer available'))
      consoleSpy.mockRestore()
    })
  })

  // Tests for serviceLink and serviceClick props
  describe('serviceLink and serviceClick props', () => {
    it('renders service name as link when serviceLink is a string', () => {
      const { container } = render(<PktHeaderService serviceName="My Service" serviceLink="https://example.com" />)
      const link = container.querySelector('a.pkt-link.pkt-header-service__service-link')
      expect(link).toBeTruthy()
      expect(link).toHaveAttribute('href', 'https://example.com')
    })

    it('renders service name as button when serviceClick is provided', () => {
      const handleClick = vi.fn()
      const { container } = render(<PktHeaderService serviceName="My Service" serviceClick={handleClick} />)
      const button = container.querySelector('button.pkt-header-service__service-link')
      expect(button).toBeTruthy()
      fireEvent.click(button!)
      expect(handleClick).toHaveBeenCalledTimes(1)
    })

    it('renders service name as span when neither serviceLink nor serviceClick is provided', () => {
      const { container } = render(<PktHeaderService serviceName="My Service" />)
      const span = container.querySelector('span.pkt-header-service__service-link')
      expect(span).toBeTruthy()
      expect(span?.tagName).toBe('SPAN')
    })

    it('prioritizes serviceLink over serviceClick when both are provided', () => {
      const handleClick = vi.fn()
      const { container } = render(
        <PktHeaderService serviceName="My Service" serviceLink="https://example.com" serviceClick={handleClick} />,
      )
      const link = container.querySelector('a.pkt-link.pkt-header-service__service-link')
      const button = container.querySelector('button.pkt-header-service__service-link')
      expect(link).toBeTruthy()
      expect(button).toBeNull()
    })
  })

  // Tests for logoClick prop
  describe('logoClick prop', () => {
    it('logoen er alltid en lenke og kaller logoClick ved klikk', () => {
      // preventDefault: jsdom kan ikke navigere
      const handleClick = vi.fn((e: React.MouseEvent) => e.preventDefault())
      const { container } = render(<PktHeaderService serviceName="Svc" logoClick={handleClick} />)
      const logoLink = container.querySelector('.pkt-header-service__logo a')
      const logoButton = container.querySelector('.pkt-header-service__logo button')
      expect(logoLink).toBeTruthy()
      expect(logoButton).toBeNull()

      fireEvent.click(logoLink!)
      expect(handleClick).toHaveBeenCalledTimes(1)
    })

    it('bruker logoLink som href når oppgitt, sammen med logoClick', () => {
      const handleClick = vi.fn((e: React.MouseEvent) => e.preventDefault())
      const { container } = render(
        <PktHeaderService serviceName="Svc" logoLink="https://example.com" logoClick={handleClick} />,
      )
      const logoLink = container.querySelector('.pkt-header-service__logo a')
      expect(logoLink).toHaveAttribute('href', 'https://example.com')

      fireEvent.click(logoLink!)
      expect(handleClick).toHaveBeenCalledTimes(1)
    })
  })

  // Tests for logout button placement on mobile vs desktop
  describe('logout button placement mobile vs desktop', () => {
    it('shows logout button in user area on desktop when logOutButtonPlacement="header"', () => {
      mockedWidth = 1400
      const handleLogout = vi.fn()
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          logOutButtonPlacement="header"
          logOut={handleLogout}
        />,
      )
      // On desktop, logout button should be in user area
      const logoutBtn = screen.getByRole('button', { name: 'Logg ut' })
      expect(logoutBtn).toBeInTheDocument()
    })

    it('shows logout button in content area on mobile when logOutButtonPlacement="header"', () => {
      mockedWidth = 500
      const handleLogout = vi.fn()
      const { container } = render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          logOutButtonPlacement="header"
          logOut={handleLogout}
        />,
      )
      const contentArea = container.querySelector('.pkt-header-service__content')
      const logoutBtn = contentArea?.querySelector('button')
      expect(logoutBtn).toBeTruthy()
      expect(screen.getByRole('button', { name: 'Logg ut' })).toBeInTheDocument()
    })

    it('does not show logout button in header areas when logOutButtonPlacement="userMenu"', () => {
      mockedWidth = 1400
      const handleLogout = vi.fn()
      render(
        <PktHeaderService
          serviceName="Svc"
          user={{ name: 'Aksel' }}
          logOutButtonPlacement="userMenu"
          logOut={handleLogout}
        />,
      )
      // When logOutButtonPlacement is userMenu, logout button should not be visible outside the menu
      // Only the user menu button should be visible, not a separate logout button
      const logoutButtons = screen.queryAllByRole('button', { name: 'Logg ut' })
      expect(logoutButtons.length).toBe(0)
    })
  })

  // Closing the slot menu on intentional clicks inside the dropdown (mobile/tablet)
  describe('slot menu closes on intentional click', () => {
    const openSlotMenu = () => {
      fireEvent.click(screen.getByRole('button', { name: 'Åpne meny' }))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'true')
    }

    beforeEach(() => {
      mockedWidth = 500
    })

    it('closes when clicking a link inside the menu', () => {
      render(
        <PktHeaderService serviceName="Svc">
          <a href="#">Gå hit</a>
        </PktHeaderService>,
      )
      openSlotMenu()
      fireEvent.click(screen.getByText('Gå hit'))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'false')
    })

    it('closes when clicking a button inside the menu', () => {
      render(
        <PktHeaderService serviceName="Svc">
          <button type="button">Gjør noe</button>
        </PktHeaderService>,
      )
      openSlotMenu()
      fireEvent.click(screen.getByText('Gjør noe'))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'false')
    })

    it('stays open when clicking a form input inside the menu', () => {
      render(
        <PktHeaderService serviceName="Svc">
          <input type="text" aria-label="Filtrer" />
        </PktHeaderService>,
      )
      openSlotMenu()
      fireEvent.click(screen.getByLabelText('Filtrer'))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'true')
    })

    it('stays open when clicking non-interactive content inside the menu', () => {
      render(
        <PktHeaderService serviceName="Svc">
          <span>Bare tekst</span>
        </PktHeaderService>,
      )
      openSlotMenu()
      fireEvent.click(screen.getByText('Bare tekst'))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'true')
    })

    it('stays open when clicking an element marked data-pkt-header-keep-open', () => {
      render(
        <PktHeaderService serviceName="Svc">
          <button type="button" data-pkt-header-keep-open>
            Vis mer
          </button>
        </PktHeaderService>,
      )
      openSlotMenu()
      fireEvent.click(screen.getByText('Vis mer'))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'true')
    })

    it('stays open when clicking a disabled button inside the menu', () => {
      render(
        <PktHeaderService serviceName="Svc">
          <button type="button" disabled>
            Utilgjengelig
          </button>
        </PktHeaderService>,
      )
      openSlotMenu()
      fireEvent.click(screen.getByText('Utilgjengelig'))
      expect(screen.getByRole('button', { name: 'Åpne meny' })).toHaveAttribute('aria-expanded', 'true')
    })
  })
})
