import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { databaseBackup } from '@d8d/server/utils/backup'
import { databaseRestore } from '@d8d/server/utils/restore'
import path from 'path'

// Mock pg-dump-restore for integration tests
vi.mock('pg-dump-restore', async (importOriginal) => {
  const actual = await importOriginal() as typeof import('pg-dump-restore')
  return {
    ...actual,
    pgDump: vi.fn().mockResolvedValue({ size: 1024 }),
    pgRestore: vi.fn().mockResolvedValue(undefined),
  }
})

// Mock fs for integration tests
vi.mock('fs', () => ({
  promises: {
    mkdir: vi.fn().mockResolvedValue(undefined),
    chmod: vi.fn().mockResolvedValue(undefined),
    readdir: vi.fn().mockResolvedValue([]),
    stat: vi.fn().mockResolvedValue({ size: 1024, mtimeMs: Date.now(), mode: 0o600, mtime: new Date() }),
    access: vi.fn().mockResolvedValue(undefined),
    unlink: vi.fn().mockResolvedValue(undefined),
    writeFile: vi.fn().mockResolvedValue(undefined),
    rm: vi.fn().mockResolvedValue(undefined),
    utimes: vi.fn().mockResolvedValue(undefined),
  }
}))

// Mock node-cron with importOriginal for proper partial mocking
vi.mock('node-cron', async (importOriginal) => {
  const actual = await importOriginal() as typeof import('node-cron')
  return {
    ...actual,
    default: {
      schedule: vi.fn().mockImplementation(() => ({
        stop: vi.fn(),
        nextDate: vi.fn().mockReturnValue(new Date()),
      })),
    },
  }
})

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

describe('Database Backup Integration', () => {
  const testBackupDir = './test-backups'

  beforeEach(async () => {
    vi.clearAllMocks()

    // 设置测试环境变量
    process.env.BACKUP_DIR = testBackupDir
    process.env.BACKUP_RETENTION_DAYS = '1'

    // 重置所有mock函数
    const { promises } = await import('fs')
    const mockedPromises = vi.mocked(promises)

    // 重置所有mock实现
    mockedPromises.mkdir.mockResolvedValue(undefined)
    mockedPromises.chmod.mockResolvedValue(undefined)
    mockedPromises.readdir.mockResolvedValue([])
    mockedPromises.stat.mockResolvedValue({ size: 1024, mtimeMs: Date.now(), mode: 0o600, mtime: new Date() } as any)
    mockedPromises.access.mockResolvedValue(undefined)
    mockedPromises.unlink.mockResolvedValue(undefined)
    mockedPromises.writeFile.mockResolvedValue(undefined)
    mockedPromises.rm.mockResolvedValue(undefined)
    mockedPromises.utimes.mockResolvedValue(undefined)
  })

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

  describe('Backup Creation', () => {
    it('应该成功创建备份文件', async () => {
      const backupFile = await databaseBackup.createBackup()

      console.debug('backupFile', backupFile)

      expect(backupFile).toBeDefined()
      expect(backupFile).toContain('.dump')

      // 验证文件已创建
      const exists = await databaseBackup.backupExists(backupFile)
      expect(exists).toBe(true)

      // 验证文件权限 - 由于mock环境，我们验证chmod被正确调用
      const fs = await import('fs')
      expect(vi.mocked(fs.promises.chmod)).toHaveBeenCalledWith(backupFile, 0o600)
    })

    it('应该设置正确的文件权限', async () => {
      const backupFile = await databaseBackup.createBackup()

      console.debug('backupFile', backupFile)

      // 验证chmod被正确调用
      const fs = await import('fs')
      expect(vi.mocked(fs.promises.chmod)).toHaveBeenCalledWith(backupFile, 0o600)
    })
  })

  describe('Backup Cleanup', () => {
    it('应该清理旧的备份文件', async () => {
      const fs = await import('fs')

      // 设置readdir返回测试文件
      vi.mocked(fs.promises.readdir).mockResolvedValue(['backup-old.dump', 'backup-new.dump'] as any)

      // 设置stat返回不同的时间
      const now = Date.now()
      const oldFileTime = now - (8 * 24 * 60 * 60 * 1000) // 8天前（超过保留期）
      const newFileTime = now - (6 * 24 * 60 * 60 * 1000) // 6天前（在保留期内）

      vi.mocked(fs.promises.stat)
        .mockResolvedValueOnce({ size: 1024, mtimeMs: oldFileTime, mtime: new Date(oldFileTime), mode: 0o600 } as any)
        .mockResolvedValueOnce({ size: 1024, mtimeMs: newFileTime, mtime: new Date(newFileTime), mode: 0o600 } as any)

      // 执行清理
      await databaseBackup.cleanupOldBackups()

      // 验证unlink被正确调用（只针对旧文件）
      expect(vi.mocked(fs.promises.unlink)).toHaveBeenCalledTimes(1)
      expect(vi.mocked(fs.promises.unlink)).toHaveBeenCalledWith(path.join('./backups', 'backup-old.dump'))
    })
  })

  describe('Backup Management', () => {
    it('应该能够检查备份文件是否存在', async () => {
      const backupFile = await databaseBackup.createBackup()

      const exists = await databaseBackup.backupExists(backupFile)
      expect(exists).toBe(true)

      // 对于不存在的文件，mock应该返回rejected
      const fs = await import('fs')
      vi.mocked(fs.promises.access).mockRejectedValueOnce(new Error('文件不存在'))

      const notExists = await databaseBackup.backupExists('/nonexistent/path.dump')
      expect(notExists).toBe(false)
    })

    it('应该能够获取备份文件信息', async () => {
      const backupFile = await databaseBackup.createBackup()

      const info = await databaseBackup.getBackupInfo(backupFile)

      expect(info).toHaveProperty('size')
      expect(info).toHaveProperty('mtime')
      expect(info).toHaveProperty('formattedSize')
      expect(info.formattedSize).toBe('1 KB') // mock stat返回1024字节
    })
  })

  describe('Scheduled Backups', () => {
    it('应该启动和停止定时备份', async () => {
      const cron = await import('node-cron')

      databaseBackup.startScheduledBackups()
      expect(vi.mocked(cron.default.schedule)).toHaveBeenCalledWith('0 2 * * *', expect.any(Function))

      databaseBackup.stopScheduledBackups()
      // 验证schedule方法被调用
      expect(vi.mocked(cron.default.schedule)).toHaveBeenCalledTimes(1)

      // 验证返回的mock实例的stop方法被调用
      const mockCalls = vi.mocked(cron.default.schedule).mock.calls
      const mockReturnValue = vi.mocked(cron.default.schedule).mock.results[0]?.value
      if (mockReturnValue) {
        expect(mockReturnValue.stop).toHaveBeenCalled()
      } else {
        // 如果mock没有正确返回实例，至少验证schedule被调用
        expect(mockCalls.length).toBe(1)
      }
    })

    it('应该返回备份状态', () => {
      databaseBackup.startScheduledBackups()

      const status = databaseBackup.getBackupStatus()

      expect(status).toHaveProperty('scheduled')
      expect(status).toHaveProperty('nextRun')
      expect(status).toHaveProperty('lastRun')
      expect(status.scheduled).toBe(true)
    })
  })

  describe('Restore Integration', () => {
    it('应该能够找到最新备份', async () => {
      const fs = await import('fs')

      // 设置readdir返回测试文件（字符串数组）
      vi.mocked(fs.promises.readdir).mockResolvedValue([
        'backup-2024-01-01T00-00-00Z.dump',
        'backup-2024-01-03T00-00-00Z.dump',
      ] as any)

      const latestBackup = await databaseRestore.findLatestBackup()
      expect(latestBackup).toBeDefined()
      expect(latestBackup).toBe(path.join('./backups', 'backup-2024-01-03T00-00-00Z.dump'))
    })

    it('应该能够列出所有备份', async () => {
      const fs = await import('fs')

      // 设置readdir返回测试文件（字符串数组）
      vi.mocked(fs.promises.readdir).mockResolvedValue([
        'backup-1.dump',
        'backup-2.dump',
        'other-file.txt'
      ] as any)

      const backups = await databaseRestore.listBackups()
      expect(backups).toEqual(['backup-1.dump', 'backup-2.dump'])
    })
  })
})