import { renderHook, act, waitFor } from '@testing-library/react-native'
import { useMembership } from './use-membership'
import { subscription, useSession } from '@/lib/auth'
import { useRouter } from 'expo-router'
import { Linking, Alert } from 'react-native'

jest.mock('@/lib/auth', () => ({
  subscription: {
    plans: jest.fn(),
    list: jest.fn(),
    create: jest.fn(),
    upgrade: jest.fn(),
    restore: jest.fn(),
    cancel: jest.fn(),
    credit: {
      topup: jest.fn(),
    },
  },
  useSession: jest.fn(),
}))

jest.mock('expo-router', () => ({
  useRouter: jest.fn(),
}))

jest.mock('react-native', () => ({
  Linking: {
    openURL: jest.fn(),
  },
  Alert: {
    alert: jest.fn(),
  },
}))

const mockUseQuery = jest.fn()
const mockUseMutation = jest.fn()

jest.mock('@tanstack/react-query', () => ({
  useQuery: (...args: any[]) => mockUseQuery(args[0]),
  useMutation: (...args: any[]) => mockUseMutation(args[0]),
}))

describe('useMembership', () => {
  const mockRouter = { push: jest.fn() }
  const mockSession = {
    user: { id: 'user-123' },
  }

  beforeEach(() => {
    jest.clearAllMocks()
    ;(useRouter as jest.Mock).mockReturnValue(mockRouter)
    ;(useSession as jest.Mock).mockReturnValue({ data: mockSession })

    mockUseQuery.mockReturnValue({
      data: null,
      isLoading: false,
      refetch: jest.fn(),
    })

    mockUseMutation.mockReturnValue({
      mutate: jest.fn(),
    })
  })

  describe('获取 Stripe 定价表数据', () => {
    it('应该成功获取定价表数据', async () => {
      const mockPricingData = {
        pricing_table_items: [
          {
            amount: '1000',
            recurring: { interval: 'month' },
            metadata: { grant_token: '1000' },
            is_highlight: true,
          },
        ],
      }

      mockUseQuery.mockReturnValue({
        data: mockPricingData,
        isLoading: false,
        refetch: jest.fn(),
      })

      const { result } = renderHook(() => useMembership())

      expect(result.current.stripePricingData).toEqual(mockPricingData)
    })
  })

  describe('获取用户订阅列表', () => {
    it('应该成功获取订阅列表', async () => {
      const mockSubscriptions = [
        {
          type: 'licenced',
          status: 'active',
          cancelAtPeriodEnd: false,
          stripeSubscriptionId: 'sub-123',
        },
      ]

      let queryCallCount = 0
      mockUseQuery.mockImplementation((config) => {
        queryCallCount++
        if (queryCallCount === 2) {
          return {
            data: mockSubscriptions,
            isLoading: false,
            refetch: jest.fn(),
          }
        }
        return {
          data: null,
          isLoading: false,
          refetch: jest.fn(),
        }
      })

      const { result } = renderHook(() => useMembership())

      expect(result.current.isLoadingSubscriptions).toBe(false)
    })

    it('应该在没有用户会话时不获取订阅', () => {
      ;(useSession as jest.Mock).mockReturnValue({ data: null })

      mockUseQuery.mockImplementation((config) => {
        if (config.enabled === false) {
          return {
            data: null,
            isLoading: false,
            refetch: jest.fn(),
          }
        }
        return {
          data: null,
          isLoading: false,
          refetch: jest.fn(),
        }
      })

      renderHook(() => useMembership())

      expect(mockUseQuery).toHaveBeenCalled()
    })
  })

  describe('创建订阅', () => {
    it('应该成功创建订阅并打开支付链接', async () => {
      const mockUrl = 'https://checkout.stripe.com/test'
      ;(subscription.create as jest.Mock).mockResolvedValue({
        data: { url: mockUrl },
        error: null,
      })

      const mutateFn = jest.fn(async (variables) => {
        const { data, error } = await subscription.create({
          priceId: variables.priceId,
          productId: variables.productId,
          successUrl: expect.stringContaining('/membership'),
          cancelUrl: expect.stringContaining('/membership?canceled=true'),
        })
        if (error) throw new Error(error.message)
        if (data?.url) await Linking.openURL(data.url)
        return data
      })

      mockUseMutation.mockReturnValue({
        mutate: mutateFn,
      })

      const { result } = renderHook(() => useMembership())

      await act(async () => {
        await result.current.createSubscription({
          priceId: 'price-123',
          productId: 'prod-123',
        })
      })

      expect(mutateFn).toHaveBeenCalled()
      expect(Linking.openURL).toHaveBeenCalledWith(mockUrl)
    })

    it('应该处理创建订阅错误', async () => {
      ;(subscription.create as jest.Mock).mockResolvedValue({
        data: null,
        error: { message: 'Failed to create subscription' },
      })

      const mutateFn = jest.fn(async (variables) => {
        try {
          const { data, error } = await subscription.create(variables)
          if (error) throw new Error(error.message)
          return data
        } catch (error) {
          Alert.alert('错误', '订阅创建失败')
          throw error
        }
      })

      mockUseMutation.mockReturnValue({
        mutate: mutateFn,
      })

      const { result } = renderHook(() => useMembership())

      await act(async () => {
        try {
          await result.current.createSubscription({
            priceId: 'price-123',
            productId: 'prod-123',
          })
        } catch (e) {
          // Expected error
        }
      })

      expect(Alert.alert).toHaveBeenCalledWith('错误', '订阅创建失败')
    })
  })

  describe('升级订阅', () => {
    it('应该成功升级订阅', async () => {
      const mockSubscriptions = [
        {
          type: 'licenced',
          status: 'active',
          cancelAtPeriodEnd: false,
          stripeSubscriptionId: 'sub-123',
        },
      ]

      let queryCallCount = 0
      mockUseQuery.mockImplementation(() => {
        queryCallCount++
        if (queryCallCount === 2) {
          return {
            data: mockSubscriptions,
            isLoading: false,
            refetch: jest.fn(),
          }
        }
        return {
          data: null,
          isLoading: false,
          refetch: jest.fn(),
        }
      })

      ;(subscription.upgrade as jest.Mock).mockResolvedValue({
        data: { success: true },
        error: null,
      })

      const mutateFn = jest.fn()
      mockUseMutation.mockReturnValue({
        mutate: mutateFn,
      })

      const { result } = renderHook(() => useMembership())

      await act(async () => {
        result.current.upgradeSubscription({ credits: 1000 })
      })

      expect(mutateFn).toHaveBeenCalled()
    })
  })

  describe('恢复订阅', () => {
    it('应该成功恢复订阅', async () => {
      const mockSubscriptions = [
        {
          type: 'licenced',
          status: 'active',
          cancelAtPeriodEnd: true,
          stripeSubscriptionId: 'sub-123',
          referenceId: 'user-123',
        },
      ]

      let queryCallCount = 0
      mockUseQuery.mockImplementation(() => {
        queryCallCount++
        if (queryCallCount === 2) {
          return {
            data: mockSubscriptions,
            isLoading: false,
            refetch: jest.fn(),
          }
        }
        return {
          data: null,
          isLoading: false,
          refetch: jest.fn(),
        }
      })

      ;(subscription.restore as jest.Mock).mockResolvedValue({
        data: { success: true },
        error: null,
      })

      const mutateFn = jest.fn()
      mockUseMutation.mockReturnValue({
        mutate: mutateFn,
      })

      const { result } = renderHook(() => useMembership())

      await act(async () => {
        result.current.restoreSubscription()
      })

      expect(mutateFn).toHaveBeenCalled()
    })
  })

  describe('充值积分', () => {
    it('应该成功充值积分并打开支付链接', async () => {
      const mockUrl = 'https://checkout.stripe.com/topup'
      const mockSubscriptions = [
        {
          type: 'metered',
          priceId: 'price-metered-123',
        },
      ]

      let queryCallCount = 0
      mockUseQuery.mockImplementation(() => {
        queryCallCount++
        if (queryCallCount === 2) {
          return {
            data: mockSubscriptions,
            isLoading: false,
            refetch: jest.fn(),
          }
        }
        return {
          data: null,
          isLoading: false,
          refetch: jest.fn(),
        }
      })

      ;(subscription.credit.topup as jest.Mock).mockResolvedValue({
        data: { url: mockUrl },
        error: null,
      })

      const mutateFn = jest.fn(async (amount) => {
        const { data, error } = await subscription.credit.topup({
          amount,
          priceId: 'price-metered-123',
          successUrl: expect.stringContaining('/membership'),
          cancelUrl: expect.stringContaining('/membership?canceled=true'),
        })
        if (error) throw new Error(error.message)
        if (data?.url) await Linking.openURL(data.url)
        return data
      })

      mockUseMutation.mockReturnValue({
        mutate: mutateFn,
      })

      const { result } = renderHook(() => useMembership())

      await act(async () => {
        await result.current.rechargeToken(1000)
      })

      expect(mutateFn).toHaveBeenCalledWith(1000)
      expect(Linking.openURL).toHaveBeenCalledWith(mockUrl)
    })

    it('应该处理无效充值金额', async () => {
      const mutateFn = jest.fn(async (amount) => {
        try {
          if (!amount || amount <= 0) throw new Error('充值金额无效')
        } catch (error) {
          Alert.alert('错误', '充值失败')
          throw error
        }
      })

      mockUseMutation.mockReturnValue({
        mutate: mutateFn,
      })

      const { result } = renderHook(() => useMembership())

      await act(async () => {
        try {
          await result.current.rechargeToken(0)
        } catch (e) {
          // Expected error
        }
      })

      expect(Alert.alert).toHaveBeenCalledWith('错误', '充值失败')
    })
  })
})
