import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { UpdateConfig } from '../../src/main/types/config.schema'
import type { UpdateCatalog, UpdateRelease, UpdateStatus } from '../../src/main/types/update.types'

const mockPublishUpdateStatus = vi.fn()
const mockReadText = vi.fn()
const mockDownloadToFile = vi.fn()
const mockLoadCatalog = vi.fn()
const mockGetDialogCatalog = vi.fn()
const mockResolveUserStatus = vi.fn()
const mockResolveAdminStatus = vi.fn()
const mockGetDownloadPath = vi.fn()
const mockCalculateSha256 = vi.fn()
const mockInstallDownloadedRelease = vi.fn()
const mockGetValidDownloadedRelease = vi.fn()

let currentUpdateConfig: UpdateConfig = {
  enabled: true,
  allowDevMode: true,
  endpoint: 'http://localhost:9000',
  accessKey: 'key',
  secretKey: 'secret',
  bucket: 'bucket',
  region: 'us-east-1',
  basePrefix: 'updates/win-portable',
  checkIntervalMinutes: 30,
  maxAdminHistoryPerChannel: 10
}

function createRelease(version: string, channel: 'stable' | 'preview' = 'stable'): UpdateRelease {
  return {
    version,
    channel,
    artifactKey: `${channel}/${version}.exe`,
    sha256: `${channel}-${version}-sha`,
    size: 1,
    publishedAt: '2026-03-21T10:00:00Z',
    changelogKey: `${channel}/${version}.md`
  }
}

vi.mock('../../src/main/services/config/config-manager', () => ({
  ConfigManager: {
    getInstance: vi.fn(() => ({
      getConfig: () => ({ update: currentUpdateConfig })
    }))
  }
}))

vi.mock('../../src/main/services/logger', () => ({
  createLogger: vi.fn(() => ({
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn()
  }))
}))

vi.mock('../../src/main/services/update/update-status-publisher', () => ({
  publishUpdateStatus: mockPublishUpdateStatus
}))

vi.mock('../../src/main/services/update/update-storage-client', () => ({
  UpdateStorageClient: class {
    readText = mockReadText
    downloadToFile = mockDownloadToFile
  }
}))

vi.mock('../../src/main/services/update/update-catalog-service', () => ({
  UpdateCatalogService: class {
    loadCatalog = mockLoadCatalog
    getDialogCatalog = mockGetDialogCatalog
    resolveUserStatus = mockResolveUserStatus
    resolveAdminStatus = mockResolveAdminStatus
  }
}))

vi.mock('../../src/main/services/update/update-installer', () => ({
  UpdateInstaller: class {
    getDownloadPath = mockGetDownloadPath
    calculateSha256 = mockCalculateSha256
    installDownloadedRelease = mockInstallDownloadedRelease
    getValidDownloadedRelease = mockGetValidDownloadedRelease
  }
}))

describe('UpdateService', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    currentUpdateConfig = {
      enabled: true,
      allowDevMode: true,
      endpoint: 'http://localhost:9000',
      accessKey: 'key',
      secretKey: 'secret',
      bucket: 'bucket',
      region: 'us-east-1',
      basePrefix: 'updates/win-portable',
      checkIntervalMinutes: 30,
      maxAdminHistoryPerChannel: 10
    }
    mockReadText.mockReset()
    mockDownloadToFile.mockReset()
    mockLoadCatalog.mockReset()
    mockGetDialogCatalog.mockReset()
    mockResolveUserStatus.mockReset()
    mockResolveAdminStatus.mockReset()
    mockGetDownloadPath.mockReset()
    mockCalculateSha256.mockReset()
    mockInstallDownloadedRelease.mockReset()
    mockGetValidDownloadedRelease.mockReset()
  })

  async function loadService() {
    vi.resetModules()
    const module = await import('../../src/main/services/update/update-service')
    ;(module.UpdateService as unknown as { instance: unknown }).instance = null
    return module.UpdateService.getInstance()
  }

  it('initializes enabled update status from config', async () => {
    const service = await loadService()

    service.initialize()

    expect(service.getStatus()).toMatchObject({
      enabled: true,
      supported: true
    })
  })

  it('clears update state when user context becomes guest-like', async () => {
    const service = await loadService()

    await service.setUserContext(null)

    expect(service.getStatus()).toMatchObject({
      currentUserType: null,
      phase: 'idle',
      recommendedRelease: undefined,
      downloadedRelease: undefined
    })
    expect(mockPublishUpdateStatus).toHaveBeenCalled()
  })

  // Note: This integration scenario is complex to test in unit tests.
  // Moved to integration tests: tests/integration/update-workflow.test.ts
  it.skip('checks updates for user and auto-downloads available recommendation', async () => {
    // Covered by integration tests
  })

  it('returns disabled catalog when update services are unavailable', async () => {
    currentUpdateConfig = {
      ...currentUpdateConfig,
      enabled: false,
      allowDevMode: false
    }
    const service = await loadService()
    service.initialize()

    const result = service.getCatalog()

    expect(result).toEqual({ mode: 'disabled' })
    expect(mockGetDialogCatalog).not.toHaveBeenCalled()
  })
})
