import path from 'node:path';

import { removeWithRetry } from '@affine-test/kit/utils/utils';
import fs from 'fs-extra';
import { v4 } from 'uuid';
import { afterEach, describe, expect, test, vi } from 'vitest';

const tmpDir = path.join(__dirname, 'tmp');
const appDataPath = path.join(tmpDir, 'app-data');

vi.doMock('@affine/electron/helper/db/ensure-db', () => ({
  ensureSQLiteDB: async () => ({
    destroy: () => {},
  }),
}));

vi.doMock('@affine/electron/helper/main-rpc', () => ({
  mainRPC: {
    getPath: async () => appDataPath,
  },
}));

afterEach(async () => {
  await removeWithRetry(tmpDir);
});

describe('list workspaces', () => {
  test('listWorkspaces (valid)', async () => {
    const { listWorkspaces } = await import(
      '@affine/electron/helper/workspace/handlers'
    );
    const workspaceId = v4();
    const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
    const meta = {
      id: workspaceId,
    };
    await fs.ensureDir(workspacePath);
    await fs.writeJSON(path.join(workspacePath, 'meta.json'), meta);
    const workspaces = await listWorkspaces();
    expect(workspaces).toEqual([[workspaceId, meta]]);
  });

  test('listWorkspaces (without meta json file)', async () => {
    const { listWorkspaces } = await import(
      '@affine/electron/helper/workspace/handlers'
    );
    const workspaceId = v4();
    const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
    await fs.ensureDir(workspacePath);
    const workspaces = await listWorkspaces();
    expect(workspaces).toEqual([
      [
        workspaceId,
        // meta file will be created automatically
        { id: workspaceId, mainDBPath: path.join(workspacePath, 'storage.db') },
      ],
    ]);
  });
});

describe('delete workspace', () => {
  test('deleteWorkspace', async () => {
    const { deleteWorkspace } = await import(
      '@affine/electron/helper/workspace/handlers'
    );
    const workspaceId = v4();
    const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
    await fs.ensureDir(workspacePath);
    await deleteWorkspace(workspaceId);
    expect(await fs.pathExists(workspacePath)).toBe(false);
    // removed workspace will be moved to deleted-workspaces
    expect(
      await fs.pathExists(
        path.join(appDataPath, 'deleted-workspaces', workspaceId)
      )
    ).toBe(true);
  });
});

describe('getWorkspaceMeta', () => {
  test('can get meta', async () => {
    const { getWorkspaceMeta } = await import(
      '@affine/electron/helper/workspace/meta'
    );
    const workspaceId = v4();
    const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
    const meta = {
      id: workspaceId,
    };
    await fs.ensureDir(workspacePath);
    await fs.writeJSON(path.join(workspacePath, 'meta.json'), meta);
    expect(await getWorkspaceMeta(workspaceId)).toEqual(meta);
  });

  test('can create meta if not exists', async () => {
    const { getWorkspaceMeta } = await import(
      '@affine/electron/helper/workspace/meta'
    );
    const workspaceId = v4();
    const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
    await fs.ensureDir(workspacePath);
    expect(await getWorkspaceMeta(workspaceId)).toEqual({
      id: workspaceId,
      mainDBPath: path.join(workspacePath, 'storage.db'),
    });
    expect(
      await fs.pathExists(path.join(workspacePath, 'meta.json'))
    ).toBeTruthy();
  });

  test('can migrate meta if db file is a link', async () => {
    const { getWorkspaceMeta } = await import(
      '@affine/electron/helper/workspace/meta'
    );
    const workspaceId = v4();
    const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
    await fs.ensureDir(workspacePath);
    const sourcePath = path.join(tmpDir, 'source.db');
    await fs.writeFile(sourcePath, 'test');

    await fs.ensureSymlink(sourcePath, path.join(workspacePath, 'storage.db'));

    expect(await getWorkspaceMeta(workspaceId)).toEqual({
      id: workspaceId,
      mainDBPath: path.join(workspacePath, 'storage.db'),
      secondaryDBPath: sourcePath,
    });

    expect(
      await fs.pathExists(path.join(workspacePath, 'meta.json'))
    ).toBeTruthy();
  });
});

test('storeWorkspaceMeta', async () => {
  const { storeWorkspaceMeta } = await import(
    '@affine/electron/helper/workspace/handlers'
  );
  const workspaceId = v4();
  const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
  await fs.ensureDir(workspacePath);
  const meta = {
    id: workspaceId,
    mainDBPath: path.join(workspacePath, 'storage.db'),
  };
  await storeWorkspaceMeta(workspaceId, meta);
  expect(await fs.readJSON(path.join(workspacePath, 'meta.json'))).toEqual(
    meta
  );
  await storeWorkspaceMeta(workspaceId, {
    secondaryDBPath: path.join(tmpDir, 'test.db'),
  });
  expect(await fs.readJSON(path.join(workspacePath, 'meta.json'))).toEqual({
    ...meta,
    secondaryDBPath: path.join(tmpDir, 'test.db'),
  });
});
