import fs from 'fs';

import {
  getArchiveDir,
  getArchiveFilePath,
  listArchiveFiles,
  getFileSize,
  cleanupOldArchiveFiles,
} from '../../src/libs/archive/store';

jest.mock('@blocklet/sdk/lib/config', () => ({
  __esModule: true,
  default: {
    env: {
      dataDir: '/tmp/test-payment-kit',
    },
  },
}));

jest.mock('fs');
jest.mock('../../src/libs/logger', () => ({
  __esModule: true,
  default: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
  },
}));

describe('archive/store', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('getArchiveDir', () => {
    it('should create archive directory if it does not exist', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(false);
      (fs.mkdirSync as jest.Mock).mockReturnValue(undefined);

      const result = getArchiveDir();

      expect(result).toContain('archive');
      expect(fs.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('archive'), { recursive: true });
    });

    it('should not create directory if it already exists', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);

      getArchiveDir();

      expect(fs.mkdirSync).not.toHaveBeenCalled();
    });
  });

  describe('getArchiveFilePath', () => {
    it('should return full path for archive file', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);

      const result = getArchiveFilePath('archive-2024.db');

      expect(result).toContain('archive-2024.db');
      expect(result).toContain('archive');
    });
  });

  describe('listArchiveFiles', () => {
    it('should return sorted list of .db files', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);
      (fs.readdirSync as jest.Mock).mockReturnValue([
        'archive-2025.db',
        'archive-2023.db',
        'archive-2024.db',
        'some-other-file.txt',
      ]);

      const result = listArchiveFiles();

      expect(result).toHaveLength(3);
      expect(result[0]).toContain('archive-2023.db');
      expect(result[1]).toContain('archive-2024.db');
      expect(result[2]).toContain('archive-2025.db');
    });

    it('should return empty array when no .db files', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);
      (fs.readdirSync as jest.Mock).mockReturnValue(['readme.txt', 'config.json']);

      const result = listArchiveFiles();

      expect(result).toHaveLength(0);
    });
  });

  describe('getFileSize', () => {
    it('should return file size in bytes', () => {
      (fs.statSync as jest.Mock).mockReturnValue({ size: 1024 });

      const result = getFileSize('/tmp/archive.db');

      expect(result).toBe(1024);
    });

    it('should return 0 when file does not exist', () => {
      (fs.statSync as jest.Mock).mockImplementation(() => {
        throw new Error('ENOENT');
      });

      const result = getFileSize('/tmp/nonexistent.db');

      expect(result).toBe(0);
    });
  });

  describe('cleanupOldArchiveFiles', () => {
    it('should not remove files when under max limit', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);
      (fs.readdirSync as jest.Mock).mockReturnValue(['archive-2023.db', 'archive-2024.db']);

      const result = cleanupOldArchiveFiles(10);

      expect(result).toHaveLength(0);
      expect(fs.unlinkSync).not.toHaveBeenCalled();
    });

    it('should remove oldest files when exceeding max limit', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);
      (fs.readdirSync as jest.Mock).mockReturnValue([
        'archive-2021.db',
        'archive-2022.db',
        'archive-2023.db',
        'archive-2024.db',
      ]);
      (fs.unlinkSync as jest.Mock).mockReturnValue(undefined);

      const result = cleanupOldArchiveFiles(2);

      expect(result).toHaveLength(2);
      expect(result).toContain('archive-2021.db');
      expect(result).toContain('archive-2022.db');
      expect(fs.unlinkSync).toHaveBeenCalledTimes(2);
    });

    it('should continue cleanup even if one file fails to delete', () => {
      (fs.existsSync as jest.Mock).mockReturnValue(true);
      (fs.readdirSync as jest.Mock).mockReturnValue(['archive-2022.db', 'archive-2023.db', 'archive-2024.db']);
      (fs.unlinkSync as jest.Mock)
        .mockImplementationOnce(() => {
          throw new Error('Permission denied');
        })
        .mockReturnValue(undefined);

      const result = cleanupOldArchiveFiles(1);

      expect(fs.unlinkSync).toHaveBeenCalledTimes(2);
      expect(result).toHaveLength(1);
      expect(result).toContain('archive-2023.db');
    });
  });
});
