import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import type { Mock } from 'vitest'
import type { UsagePlanInfo } from '@/app/components/billing/type'
import type { AppContextValue } from '@/context/app-context'
import type { ProviderContextState } from '@/context/provider-context'
import type { ICurrentWorkspace, LangGeniusVersionResponse } from '@/models/common'
import { render, screen } from '@testing-library/react'
import { Plan } from '@/app/components/billing/type'
import { mailToSupport } from '@/app/components/header/utils/util'
import { useAppContext } from '@/context/app-context'
import { baseProviderContextValue, useProviderContext } from '@/context/provider-context'
import AppsFull from '../index'

vi.mock('@/context/app-context', () => ({
  useAppContext: vi.fn(),
}))

vi.mock('@/context/provider-context', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/context/provider-context')>()
  return {
    ...actual,
    useProviderContext: vi.fn(),
  }
})

vi.mock('@/context/modal-context', () => ({
  useModalContext: () => ({
    setShowPricingModal: vi.fn(),
  }),
}))

vi.mock('@/app/components/header/utils/util', () => ({
  mailToSupport: vi.fn(),
}))

const buildUsage = (overrides: Partial<UsagePlanInfo> = {}): UsagePlanInfo => ({
  buildApps: 0,
  teamMembers: 0,
  annotatedResponse: 0,
  documentsUploadQuota: 0,
  apiRateLimit: 0,
  triggerEvents: 0,
  vectorSpace: 0,
  ...overrides,
})

const buildProviderContext = (overrides: Partial<ProviderContextState> = {}): ProviderContextState => ({
  ...baseProviderContextValue,
  plan: {
    ...baseProviderContextValue.plan,
    type: Plan.sandbox,
    usage: buildUsage({ buildApps: 2 }),
    total: buildUsage({ buildApps: 10 }),
    reset: {
      apiRateLimit: null,
      triggerEvents: null,
    },
  },
  ...overrides,
})

const buildAppContext = (overrides: Partial<AppContextValue> = {}): AppContextValue => {
  const userProfile: GetAccountProfileResponse = {
    id: 'user-id',
    name: 'Test User',
    email: 'user@example.com',
    avatar: '',
    avatar_url: '',
    is_password_set: false,
  }
  const currentWorkspace: ICurrentWorkspace = {
    id: 'workspace-id',
    name: 'Workspace',
    plan: '',
    status: '',
    created_at: 0,
    role: 'normal',
    providers: [],
    trial_credits: 200,
    trial_credits_used: 0,
    next_credit_reset_date: 0,
  }
  const langGeniusVersionInfo: LangGeniusVersionResponse = {
    current_env: '',
    current_version: '1.0.0',
    latest_version: '',
    release_date: '',
    release_notes: '',
    version: '',
    can_auto_update: false,
  }
  const base: Omit<AppContextValue, 'useSelector'> = {
    userProfile,
    currentWorkspace,
    isCurrentWorkspaceManager: false,
    isCurrentWorkspaceOwner: false,
    isCurrentWorkspaceEditor: false,
    isCurrentWorkspaceDatasetOperator: false,
    mutateUserProfile: vi.fn(),
    mutateCurrentWorkspace: vi.fn(),
    langGeniusVersionInfo,
    isLoadingCurrentWorkspace: false,
    isValidatingCurrentWorkspace: false,
  }
  const useSelector: AppContextValue['useSelector'] = selector => selector({ ...base, useSelector })
  return {
    ...base,
    useSelector,
    ...overrides,
  }
}

describe('AppsFull', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    ;(useProviderContext as Mock).mockReturnValue(buildProviderContext())
    ;(useAppContext as Mock).mockReturnValue(buildAppContext())
    ;(mailToSupport as Mock).mockReturnValue('mailto:support@example.com')
  })

  // Rendering behavior for non-team plans.
  describe('Rendering', () => {
    it('should render the sandbox messaging and upgrade button', () => {
      render(<AppsFull loc="billing_dialog" />)

      expect(screen.getByText('billing.apps.fullTip1')).toBeInTheDocument()
      expect(screen.getByText('billing.apps.fullTip1des')).toBeInTheDocument()
      expect(screen.getByText('billing.upgradeBtn.encourageShort')).toBeInTheDocument()
      expect(screen.getByText('2/10')).toBeInTheDocument()
    })
  })

  describe('Props', () => {
    it('should render team messaging and contact button for non-sandbox plans', () => {
      ;(useProviderContext as Mock).mockReturnValue(buildProviderContext({
        plan: {
          ...baseProviderContextValue.plan,
          type: Plan.team,
          usage: buildUsage({ buildApps: 8 }),
          total: buildUsage({ buildApps: 10 }),
          reset: {
            apiRateLimit: null,
            triggerEvents: null,
          },
        },
      }))
      render(<AppsFull loc="billing_dialog" />)

      expect(screen.getByText('billing.apps.fullTip2')).toBeInTheDocument()
      expect(screen.getByText('billing.apps.fullTip2des')).toBeInTheDocument()
      expect(screen.queryByText('billing.upgradeBtn.encourageShort')).not.toBeInTheDocument()
      expect(screen.getByRole('link', { name: 'billing.apps.contactUs' })).toHaveAttribute('href', 'mailto:support@example.com')
      expect(mailToSupport).toHaveBeenCalledWith('user@example.com', Plan.team, '1.0.0')
    })

    it('should render upgrade button for professional plans', () => {
      ;(useProviderContext as Mock).mockReturnValue(buildProviderContext({
        plan: {
          ...baseProviderContextValue.plan,
          type: Plan.professional,
          usage: buildUsage({ buildApps: 4 }),
          total: buildUsage({ buildApps: 10 }),
          reset: {
            apiRateLimit: null,
            triggerEvents: null,
          },
        },
      }))

      render(<AppsFull loc="billing_dialog" />)

      expect(screen.getByText('billing.apps.fullTip1')).toBeInTheDocument()
      expect(screen.getByText('billing.upgradeBtn.encourageShort')).toBeInTheDocument()
      expect(screen.queryByText('billing.apps.contactUs')).not.toBeInTheDocument()
    })

    it('should render contact button for enterprise plans', () => {
      ;(useProviderContext as Mock).mockReturnValue(buildProviderContext({
        plan: {
          ...baseProviderContextValue.plan,
          type: Plan.enterprise,
          usage: buildUsage({ buildApps: 9 }),
          total: buildUsage({ buildApps: 10 }),
          reset: {
            apiRateLimit: null,
            triggerEvents: null,
          },
        },
      }))

      render(<AppsFull loc="billing_dialog" />)

      expect(screen.getByText('billing.apps.fullTip1')).toBeInTheDocument()
      expect(screen.queryByText('billing.upgradeBtn.encourageShort')).not.toBeInTheDocument()
      expect(screen.getByRole('link', { name: 'billing.apps.contactUs' })).toHaveAttribute('href', 'mailto:support@example.com')
      expect(mailToSupport).toHaveBeenCalledWith('user@example.com', Plan.enterprise, '1.0.0')
    })
  })

  describe('Edge Cases', () => {
    it('applies neutral / warning / error tone at distinct usage levels', () => {
      const findToneClass = (used: number, total: number) => {
        ;(useProviderContext as Mock).mockReturnValue(buildProviderContext({
          plan: {
            ...baseProviderContextValue.plan,
            type: Plan.sandbox,
            usage: buildUsage({ buildApps: used }),
            total: buildUsage({ buildApps: total }),
            reset: { apiRateLimit: null, triggerEvents: null },
          },
        }))
        const { container, unmount } = render(<AppsFull loc="billing_dialog" />)
        const indicator = container.querySelector(
          '[class*="bg-components-progress-"]:not([class*="progress-bar-bg"])',
        )
        const className = indicator?.className ?? ''
        unmount()
        return className
      }

      expect(findToneClass(2, 10)).toContain('bg-components-progress-bar-progress-solid')
      expect(findToneClass(6, 10)).toContain('bg-components-progress-warning-progress')
      expect(findToneClass(8, 10)).toContain('bg-components-progress-error-progress')
    })
  })
})
