import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'

import ProfilePage from './ProfilePage.vue'
import { useUserStore } from '@/stores/user'

// Mock vue-router
const mockRouterPush = vi.fn()
vi.mock('vue-router', () => ({
  useRouter: () => ({
    push: mockRouterPush,
  }),
  useRoute: () => ({
    path: '/profile',
    name: 'Profile',
    query: {},
    params: {},
  }),
}))

// Mock useAuth composable
const mockLogout = vi.fn()
const mockLogoutAll = vi.fn()
vi.mock('@/composables/useAuth', () => ({
  useAuth: () => ({
    logout: mockLogout,
    logoutAll: mockLogoutAll,
    isLoading: { value: false },
  }),
}))

// Mock API client
vi.mock('@/api/client', () => ({
  apiClient: {
    get: vi.fn().mockResolvedValue({ activeSessionCount: 2 }),
    delete: vi.fn().mockResolvedValue(undefined),
  },
}))

// Mock config
vi.mock('@/config', () => ({
  config: {
    apiBaseUrl: 'http://localhost:8000',
    wsUrl: 'http://localhost:8000',
    oauthRedirectUri: 'http://localhost:5173/auth/callback',
    isDev: true,
    isProd: false,
  },
}))

// Mock fetch for auth store
global.fetch = vi.fn().mockResolvedValue({
  ok: true,
  json: () => Promise.resolve({}),
})

// Mock window.location
const mockLocation = {
  href: '',
  origin: 'http://localhost:5173',
}
Object.defineProperty(window, 'location', {
  value: mockLocation,
  writable: true,
  configurable: true,
})

// Mock window.confirm
global.confirm = vi.fn()

describe('ProfilePage', () => {
  function createProfileData() {
    return {
      id: 'user-1',
      displayName: 'Test User',
      avatarUrl: null,
      hasStarterDeck: true,
      createdAt: '2026-01-01T00:00:00Z',
      linkedAccounts: [
        {
          provider: 'google' as const,
          providerUserId: 'g123',
          email: 'test@gmail.com',
          linkedAt: '2026-01-01T00:00:00Z',
        },
      ],
    }
  }

  beforeEach(() => {
    setActivePinia(createPinia())
    mockRouterPush.mockReset()
    mockLogout.mockReset()
    mockLogoutAll.mockReset()
    mockLocation.href = ''
    vi.mocked(global.confirm).mockReturnValue(false)

    // Set up user store with profile
    const userStore = useUserStore()
    userStore.profile = createProfileData()
  })

  afterEach(() => {
    vi.clearAllMocks()
  })

  describe('rendering', () => {
    it('shows loading state while fetching profile', () => {
      /**
       * Test loading state display.
       *
       * While the profile is being fetched, users should see
       * a loading indicator instead of an empty page.
       */
      const userStore = useUserStore()
      userStore.profile = null
      userStore.isLoading = true

      const wrapper = mount(ProfilePage)

      expect(wrapper.find('.animate-spin').exists()).toBe(true)
    })

    it('shows profile data when loaded', async () => {
      /**
       * Test profile data display.
       *
       * Once loaded, the profile page should show the user's
       * display name and other relevant information.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).toContain('Test User')
      expect(wrapper.text()).toContain('Profile')
    })

    it('shows avatar initial when no avatar URL', async () => {
      /**
       * Test avatar fallback display.
       *
       * When users don't have an avatar image, we show their
       * name initial as a fallback.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).toContain('T') // First letter of "Test User"
    })

    it('shows member since date', async () => {
      /**
       * Test member date display.
       *
       * Users should see when they joined the platform.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).toContain('Member since')
    })
  })

  describe('linked accounts', () => {
    it('displays linked accounts list', async () => {
      /**
       * Test linked accounts display.
       *
       * Users should see all their linked OAuth providers
       * with relevant details.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).toContain('Linked Accounts')
      expect(wrapper.text()).toContain('Google')
    })

    it('shows link button for unlinked providers', async () => {
      /**
       * Test link additional provider button.
       *
       * If a provider isn't linked, users should see an option
       * to link it.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      // Google is linked, so Discord should have a link button
      expect(wrapper.text()).toContain('Link Discord')
    })

    it('hides link buttons when all providers linked', async () => {
      /**
       * Test hiding link buttons when all linked.
       *
       * If both Google and Discord are linked, no additional
       * link buttons should appear.
       */
      const userStore = useUserStore()
      userStore.profile!.linkedAccounts = [
        { provider: 'google', providerUserId: 'g123', email: 'test@gmail.com', linkedAt: '2026-01-01T00:00:00Z' },
        { provider: 'discord', providerUserId: 'd456', email: null, linkedAt: '2026-01-02T00:00:00Z' },
      ]

      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).not.toContain('Link Google')
      expect(wrapper.text()).not.toContain('Link Discord')
    })
  })

  describe('logout', () => {
    it('shows logout buttons in Account Actions section', async () => {
      /**
       * Test that logout buttons are present.
       *
       * The profile page should show both a regular logout button
       * and a logout all devices button.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      // Check the section exists and contains the expected elements
      expect(wrapper.text()).toContain('Account Actions')
      expect(wrapper.text()).toContain('Logout')
      expect(wrapper.text()).toContain('Logout All Devices')
    })

    it('has logout action handlers defined', async () => {
      /**
       * Test that logout handlers are available.
       *
       * The page should have click handlers that call the
       * auth composable's logout methods.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      // The logout buttons should exist and be clickable
      const buttons = wrapper.findAll('button')
      expect(buttons.length).toBeGreaterThan(0)

      // Verify the handlers exist by checking the component renders properly
      expect(wrapper.html()).toContain('bg-red-600') // Logout button styling
      expect(wrapper.html()).toContain('bg-red-900') // Logout all button styling
    })
  })

  describe('session info', () => {
    it('displays session count section', async () => {
      /**
       * Test session info section display.
       *
       * Users should see information about their active sessions.
       */
      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).toContain('Sessions')
    })
  })

  describe('error handling', () => {
    it('shows error state when profile fetch fails', async () => {
      /**
       * Test error display on profile fetch failure.
       *
       * If the profile cannot be loaded, users should see
       * an error message explaining the problem.
       */
      const userStore = useUserStore()
      userStore.profile = null

      // Mock fetchProfile to fail and set error
      vi.spyOn(userStore, 'fetchProfile').mockImplementation(async () => {
        userStore.error = 'Failed to load profile'
        return false
      })

      const wrapper = mount(ProfilePage)

      await flushPromises()

      expect(wrapper.text()).toContain('Failed to load profile')
    })
  })
})
