import { describe, it, expect, vi, beforeEach } from 'vitest'
import { ref, computed, readonly } from 'vue'

// --- Nuxt auto-import stubs ---

vi.stubGlobal('computed', computed)
vi.stubGlobal('readonly', readonly)

// useState: return a fresh ref per key, reset between tests
const stateStore = {}
vi.stubGlobal('useState', (key, init) => {
  if (!stateStore[key]) {
    stateStore[key] = ref(init ? init() : null)
  }
  return stateStore[key]
})

function resetState() {
  for (const key of Object.keys(stateStore)) {
    delete stateStore[key]
  }
}

// $fetch mock
const fetchMock = vi.fn()
vi.stubGlobal('$fetch', fetchMock)

// --- Tests ---

describe('useOnboarding', () => {
  let useOnboarding

  beforeEach(async () => {
    resetState()
    fetchMock.mockReset()
    // Default: status endpoint returns all-false goals
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: false,
            hasVisitedEvent: false,
            hasEngagedBoard: false,
            hasClickedWiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    // Re-import to get clean module state
    vi.resetModules()
    const mod = await import('../../../app/composables/useOnboarding.js')
    useOnboarding = mod.useOnboarding
  })

  // 9.1: Initializes goals from API response
  it('9.1: initializes goals from API response', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: false,
            hasEngagedBoard: true,
            hasClickedWiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    const { goals, loading } = useOnboarding()

    // Wait for fetchStatus to complete
    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(goals.value.hasProfileTags).toBe(true)
    expect(goals.value.hasVisitedEvent).toBe(false)
    expect(goals.value.hasEngagedBoard).toBe(true)
    expect(goals.value.hasClickedWiki).toBe(false)
  })

  // 9.2: isComplete true when all goals true
  it('9.2: isComplete is true when all goals are true', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: true,
            hasClickedWiki: true,
          },
          completedAt: '2026-04-01T00:00:00Z',
        })
      }
      if (url === '/api/events/recommended') return Promise.resolve([])
      if (url === '/api/wiki/recommended') return Promise.resolve([])
      return Promise.resolve(null)
    })

    const { isComplete, completedAt, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(isComplete.value).toBe(true)
    expect(completedAt.value).toBe('2026-04-01T00:00:00Z')
  })

  // 9.3: isComplete false with partial goals
  it('9.3: isComplete is false with partial goals', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: false,
            hasClickedWiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    const { isComplete, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(isComplete.value).toBe(false)
  })

  // 9.4: currentSuggestion returns profile tags when no tags set (priority 1)
  it('9.4: currentSuggestion returns profile tags when no tags set', async () => {
    const { currentSuggestion, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('profileTags')
    expect(currentSuggestion.value.action).toBe('/member/profile')
    expect(currentSuggestion.value.actionText).toBe('Set up tags')
  })

  // 9.5: currentSuggestion returns event when tags set but no event visit (priority 2)
  it('9.5: currentSuggestion returns event when tags set but no event visit', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: false,
            hasEngagedBoard: false,
            hasClickedWiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    const { currentSuggestion, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('visitEvent')
    expect(currentSuggestion.value.action).toBe('/events')
    expect(currentSuggestion.value.actionText).toBe('Browse events')
  })

  // 9.6: currentSuggestion returns board when tags + event done (priority 3)
  it('9.6: currentSuggestion returns board when tags + event done', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: false,
            hasClickedWiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    const { currentSuggestion, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('board')
    expect(currentSuggestion.value.action).toBe('/board')
  })

  // 9.7: currentSuggestion returns wiki when only wiki remaining (priority 4)
  it('9.7: currentSuggestion returns wiki when only wiki remaining', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: true,
            hasClickedWiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    const { currentSuggestion, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('wiki')
    expect(currentSuggestion.value.action).toBe('https://wiki.ghostguild.org')
    expect(currentSuggestion.value.isExternal).toBe(true)
  })

  // 9.8: trackGoal fires POST and is skipped when complete
  it('9.8: trackGoal fires POST and is skipped when complete', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: true,
            hasClickedWiki: true,
          },
          completedAt: '2026-04-01T00:00:00Z',
        })
      }
      if (url === '/api/events/recommended') return Promise.resolve([])
      if (url === '/api/wiki/recommended') return Promise.resolve([])
      return Promise.resolve(null)
    })

    const { trackGoal, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    await trackGoal('eventPageVisited')

    // Should NOT have called the track endpoint since isComplete is true
    const trackCalls = fetchMock.mock.calls.filter(
      (c) => c[0] === '/api/onboarding/track'
    )
    expect(trackCalls).toHaveLength(0)
  })

  // 9.9: Suggestion mode uses injectable pickCategory
  it('9.9: suggestion mode uses injectable pickCategory', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: true,
            hasClickedWiki: true,
          },
          completedAt: '2026-04-01T00:00:00Z',
        })
      }
      if (url === '/api/events/recommended') {
        return Promise.resolve([{ _id: 'e1', title: 'Game Jam' }])
      }
      if (url === '/api/wiki/recommended') {
        return Promise.resolve([{ title: 'Co-op Guide', url: 'https://wiki.example.com/coop' }])
      }
      return Promise.resolve(null)
    })

    // Always pick 'wiki'
    const { currentSuggestion, loading } = useOnboarding({
      pickCategory: () => 'wiki',
    })

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('wiki')
    expect(currentSuggestion.value.text).toContain('Co-op Guide')
    expect(currentSuggestion.value.isExternal).toBe(true)
  })

  // 9.10: Suggestion mode falls back when selected category empty
  it('9.10: suggestion mode falls back when selected category empty', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: true,
            hasClickedWiki: true,
          },
          completedAt: '2026-04-01T00:00:00Z',
        })
      }
      if (url === '/api/events/recommended') {
        return Promise.resolve([{ _id: 'e1', title: 'Game Jam' }])
      }
      if (url === '/api/wiki/recommended') {
        return Promise.resolve([])
      }
      return Promise.resolve(null)
    })

    // pickCategory only gets non-empty categories, so it should only see 'events'
    const pickedCategories = []
    const { currentSuggestion, loading } = useOnboarding({
      pickCategory: (cats) => {
        pickedCategories.push([...cats])
        return cats[0]
      },
    })

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    // Access computed first to trigger pickCategory
    expect(currentSuggestion.value.key).toBe('event')
    expect(currentSuggestion.value.text).toContain('Game Jam')
    // Only events had results, so pickCategory should only have received ['events']
    expect(pickedCategories[0]).toEqual(['events'])
  })

  // 9.11: Suggestion mode shows fallback when all categories empty
  it('9.11: suggestion mode shows fallback when all categories empty', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: true,
            hasVisitedEvent: true,
            hasEngagedBoard: true,
            hasClickedWiki: true,
          },
          completedAt: '2026-04-01T00:00:00Z',
        })
      }
      if (url === '/api/events/recommended') return Promise.resolve([])
      if (url === '/api/wiki/recommended') return Promise.resolve([])
      return Promise.resolve(null)
    })

    const { currentSuggestion, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('empty')
    expect(currentSuggestion.value.text).toBe('No suggestions right now')
  })

  // Skip: skipping a suggestion advances to the next incomplete, non-skipped one
  it('skipSuggestion advances to the next suggestion and posts to track', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: false,
            hasVisitedEvent: false,
            hasEngagedBoard: false,
            hasClickedWiki: false,
          },
          skipped: {
            profileTags: false,
            visitEvent: false,
            board: false,
            wiki: false,
          },
          completedAt: null,
        })
      }
      return Promise.resolve(null)
    })

    const { currentSuggestion, skipSuggestion, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(currentSuggestion.value.key).toBe('profileTags')

    await skipSuggestion('profileTags')

    // Next priority advances to visitEvent
    expect(currentSuggestion.value.key).toBe('visitEvent')

    const trackCalls = fetchMock.mock.calls.filter(
      (c) => c[0] === '/api/onboarding/track'
    )
    expect(trackCalls).toHaveLength(1)
    expect(trackCalls[0][1]).toMatchObject({
      method: 'POST',
      body: { skip: 'profileTags' },
    })
  })

  // Skip: skipped suggestions count toward isComplete
  it('all skipped counts as complete for suggestion widget', async () => {
    fetchMock.mockImplementation((url) => {
      if (url === '/api/onboarding/status') {
        return Promise.resolve({
          goals: {
            hasProfileTags: false,
            hasVisitedEvent: false,
            hasEngagedBoard: false,
            hasClickedWiki: false,
          },
          skipped: {
            profileTags: true,
            visitEvent: true,
            board: true,
            wiki: true,
          },
          completedAt: null,
        })
      }
      if (url === '/api/events/recommended') return Promise.resolve([])
      if (url === '/api/wiki/recommended') return Promise.resolve([])
      return Promise.resolve(null)
    })

    const { isComplete, loading } = useOnboarding()

    await vi.waitFor(() => {
      expect(loading.value).toBe(false)
    })

    expect(isComplete.value).toBe(true)
  })
})
