import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { DatabaseRestore } from '../../../../src/server/utils/restore'
import path from 'path'

// Mock pg-dump-restore
vi.mock('pg-dump-restore', () => ({
  pgRestore: vi.fn().mockResolvedValue(undefined),
}))

// Mock fs with importOriginal for partial mocking
vi.mock('fs', async (importOriginal) => {
  const actual = await importOriginal() as typeof import('fs')
  return {
    ...actual,
    promises: {
      ...actual.promises,
      readdir: vi.fn().mockResolvedValue([]),
      access: vi.fn().mockResolvedValue(undefined),
      stat: vi.fn().mockResolvedValue({ size: 1024, mtime: new Date() }),
    },
  }
})

// Mock logger
vi.mock('../../../../src/server/utils/logger', () => ({
  logger: {
    db: vi.fn(),
    error: vi.fn(),
    api: vi.fn(),
    middleware: vi.fn(),
  },
}))

describe('DatabaseRestore', () => {
  let restore: DatabaseRestore

  beforeEach(() => {
    vi.clearAllMocks()
    restore = new DatabaseRestore()
  })

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

  describe('getDbConfig', () => {
    it('应该返回正确的数据库配置', () => {
      process.env.DB_HOST = 'test-host'
      process.env.DB_PORT = '5433'
      process.env.DB_DATABASE = 'test-db'
      process.env.DB_USERNAME = 'test-user'
      process.env.DB_PASSWORD = 'test-password'

      const config = (restore as any).getDbConfig()

      expect(config).toEqual({
        host: 'test-host',
        port: 5433,
        database: 'test-db',
        username: 'test-user',
        password: 'test-password',
      })
    })

    it('应该使用默认值当环境变量未设置时', () => {
      delete process.env.DB_HOST
      delete process.env.DB_PORT
      delete process.env.DB_DATABASE
      delete process.env.DB_USERNAME
      delete process.env.DB_PASSWORD

      const config = (restore as any).getDbConfig()

      expect(config).toEqual({
        host: 'localhost',
        port: 5432,
        database: 'postgres',
        username: 'postgres',
        password: '',
      })
    })
  })

  describe('findLatestBackup', () => {
    it('应该返回最新的备份文件', async () => {
      const fs = await import('fs')

      vi.mocked(fs.promises.readdir).mockResolvedValue([
        'backup-2024-01-01T00-00-00Z.dump',
        'backup-2024-01-03T00-00-00Z.dump',
        'backup-2024-01-02T00-00-00Z.dump',
      ] as any)

      const latest = await restore.findLatestBackup()

      expect(latest).toBe(path.join('./backups', 'backup-2024-01-03T00-00-00Z.dump'))
    })

    it('应该返回null当没有备份文件时', async () => {
      const fs = await import('fs')

      vi.mocked(fs.promises.readdir).mockResolvedValue([
        'some-other-file.txt'
      ] as any)

      const latest = await restore.findLatestBackup()

      expect(latest).toBeNull()
    })

    it('应该在读取目录失败时返回null', async () => {
      const fs = await import('fs')
      const { logger } = await import('../../../../src/server/utils/logger')

      vi.mocked(fs.promises.readdir).mockRejectedValueOnce(new Error('读取目录失败'))

      const latest = await restore.findLatestBackup()

      expect(latest).toBeNull()
      expect(logger.error).toHaveBeenCalled()
    })
  })

  describe('listBackups', () => {
    it('应该返回所有备份文件列表', async () => {
      const fs = await import('fs')

      vi.mocked(fs.promises.readdir).mockResolvedValue([
        'backup-2024-01-01.dump',
        'some-other-file.txt',
        'backup-2024-01-02.dump',
      ] as any)

      const backups = await restore.listBackups()

      expect(backups).toEqual([
        'backup-2024-01-01.dump',
        'backup-2024-01-02.dump',
      ])
    })

    it('应该在读取目录失败时返回空数组', async () => {
      const fs = await import('fs')
      const { logger } = await import('../../../../src/server/utils/logger')

      vi.mocked(fs.promises.readdir).mockRejectedValueOnce(new Error('读取目录失败'))

      const backups = await restore.listBackups()

      expect(backups).toEqual([])
      expect(logger.error).toHaveBeenCalled()
    })
  })

  describe('backupExists', () => {
    it('应该返回true当备份文件存在时', async () => {
      const fs = await import('fs')

      const exists = await restore.backupExists('/path/to/backup.dump')

      expect(exists).toBe(true)
      expect(fs.promises.access).toHaveBeenCalledWith('/path/to/backup.dump')
    })

    it('应该返回false当备份文件不存在时', async () => {
      const fs = await import('fs')
      vi.mocked(fs.promises.access).mockRejectedValueOnce(new Error('文件不存在'))

      const exists = await restore.backupExists('/path/to/backup.dump')

      expect(exists).toBe(false)
    })
  })

  describe('getBackupInfo', () => {
    it('应该返回备份文件信息', async () => {
      const fs = await import('fs')
      const testDate = new Date()

      vi.mocked(fs.promises.stat).mockResolvedValueOnce({
        size: 1048576,
        mtime: testDate,
      } as any)

      const info = await restore.getBackupInfo('/path/to/backup.dump')

      expect(info).toEqual({
        size: 1048576,
        mtime: testDate,
        formattedSize: '1 MB',
      })
    })

    it('应该在获取信息失败时抛出错误', async () => {
      const fs = await import('fs')

      vi.mocked(fs.promises.stat).mockRejectedValueOnce(new Error('获取文件信息失败'))

      await expect(restore.getBackupInfo('/path/to/backup.dump')).rejects.toThrow('获取备份信息失败')
    })
  })

  describe('formatFileSize', () => {
    it('应该正确格式化文件大小', () => {
      const formatFileSize = (restore as any).formatFileSize

      expect(formatFileSize(0)).toBe('0 B')
      expect(formatFileSize(1024)).toBe('1 KB')
      expect(formatFileSize(1048576)).toBe('1 MB')
      expect(formatFileSize(1073741824)).toBe('1 GB')
    })
  })
})