import { WORKSPACE_STORAGE_TYPE } from '@learnitive/constants';
import { createTestEnvironment } from '@learnitive/test-utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

describe('WorkspaceOpsService', () => {
  let controller: AbortController;

  beforeEach(() => {
    controller = new AbortController();
  });

  afterEach(() => {
    controller.abort();
  });

  it('caches workspace info by workspace name without aliasing query options', async () => {
    const testEnv = createTestEnvironment({ controller });
    const services = testEnv.instantiateAll();
    await testEnv.mountAll();

    const foo = await services.workspaceOps.createWorkspaceInfo({
      name: 'foo',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: {},
    });
    const foobrowser = await services.workspaceOps.createWorkspaceInfo({
      name: 'foobrowser',
      type: WORKSPACE_STORAGE_TYPE.Memory,
      metadata: {},
    });
    await services.workspaceOps.createWorkspaceInfo({
      name: 'deleted',
      type: WORKSPACE_STORAGE_TYPE.Memory,
      metadata: {},
    });
    await services.workspaceOps.deleteWorkspaceInfo('deleted');

    await expect(
      services.workspaceOps.getWorkspaceInfo('foo', {
        type: WORKSPACE_STORAGE_TYPE.Browser,
      }),
    ).resolves.toEqual(foo);
    await expect(
      services.workspaceOps.getWorkspaceInfo('foo', {
        type: WORKSPACE_STORAGE_TYPE.Memory,
      }),
    ).resolves.toBeUndefined();
    await expect(
      services.workspaceOps.getWorkspaceInfo('foobrowser'),
    ).resolves.toEqual(foobrowser);
    await expect(
      services.workspaceOps.getWorkspaceInfo('deleted', {
        allowDeleted: true,
      }),
    ).resolves.toMatchObject({ name: 'deleted', deleted: true });
    await expect(
      services.workspaceOps.getWorkspaceInfo('deleted'),
    ).resolves.toBeUndefined();
  });

  it('caps anonymous Free project creation at the configured limit without locking existing work', async () => {
    const testEnv = createTestEnvironment({ controller });
    const services = testEnv.instantiateAll();
    await testEnv.mountAll();
    vi.spyOn(services.projectEntitlements, 'projectLimit').mockResolvedValue(5);
    vi.spyOn(services.projectEntitlements, 'accountProjectIds').mockResolvedValue(new Set());

    for (let index = 1; index <= 5; index += 1) {
      await services.workspaceOps.createWorkspaceInfo({
        name: `free-${index}`,
        type: WORKSPACE_STORAGE_TYPE.Browser,
        metadata: {},
      });
    }

    await expect(services.workspaceOps.getBrowserProjectLimitStatus()).resolves.toEqual({ current: 5, limit: 5, reached: true });
    await expect(services.workspaceOps.createWorkspaceInfo({
      name: 'free-6',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: {},
    })).rejects.toMatchObject({ type: 'error::workspace:project-limit-reached' });
    await expect(services.workspaceOps.getWorkspaceInfo('free-1')).resolves.toMatchObject({ name: 'free-1' });
    await services.workspaceOps.deleteWorkspaceInfo('free-5');
    await expect(services.workspaceOps.createWorkspaceInfo({
      name: 'free-replacement',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: {},
    })).resolves.toMatchObject({ name: 'free-replacement' });
  });

  it('uses the configured Free limit and does not grant a second allowance after sign-in', async () => {
    const testEnv = createTestEnvironment({ controller });
    const services = testEnv.instantiateAll();
    await testEnv.mountAll();
    vi.spyOn(services.projectEntitlements, 'projectLimit').mockResolvedValue(3);
    vi.spyOn(services.projectEntitlements, 'accountProjectIds').mockResolvedValue(new Set(['remote-1', 'remote-2']));

    await services.workspaceOps.createWorkspaceInfo({
      name: 'local-owned',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: {},
    });
    await expect(services.workspaceOps.getBrowserProjectLimitStatus()).resolves.toEqual({ current: 3, limit: 3, reached: true });
    await expect(services.workspaceOps.createWorkspaceInfo({
      name: 'would-be-fourth',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: {},
    })).rejects.toMatchObject({ type: 'error::workspace:project-limit-reached' });
  });

  it('expired Monthly entitlement falls back to the Free project cap', async () => {
    const testEnv = createTestEnvironment({ controller });
    const services = testEnv.instantiateAll();
    await testEnv.mountAll();
    vi.spyOn(services.projectEntitlements, 'projectLimit').mockResolvedValue(5);
    vi.spyOn(services.projectEntitlements, 'accountProjectIds').mockResolvedValue(new Set());
    testEnv.store.set(services.projectEntitlements.$state, {
      status: 'pro',
      tier: 'pro',
      refreshing: false,
      accountSource: 'license',
      accountExpiresAt: 1,
    });

    for (let index = 1; index <= 5; index += 1) {
      await services.workspaceOps.createWorkspaceInfo({
        name: `expired-monthly-${index}`,
        type: WORKSPACE_STORAGE_TYPE.Browser,
        metadata: {},
      });
    }
    await expect(services.workspaceOps.createWorkspaceInfo({
      name: 'expired-monthly-6',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: {},
    })).rejects.toMatchObject({ type: 'error::workspace:project-limit-reached' });
  });

  it('excludes joined collaboration materialization from the owned-project count', async () => {
    const testEnv = createTestEnvironment({ controller });
    const services = testEnv.instantiateAll();
    await testEnv.mountAll();
    vi.spyOn(services.projectEntitlements, 'projectLimit').mockResolvedValue(5);
    vi.spyOn(services.projectEntitlements, 'accountProjectIds').mockResolvedValue(new Set());

    for (let index = 1; index <= 3; index += 1) {
      await services.workspaceOps.createWorkspaceInfo({
        name: `owned-${index}`,
        type: WORKSPACE_STORAGE_TYPE.Browser,
        metadata: {},
      });
    }
    await expect(services.workspaceOps.createWorkspaceInfo({
      name: 'joined-room',
      type: WORKSPACE_STORAGE_TYPE.Browser,
      metadata: { learnitiveCollaboration: { source: 'joined' } },
    }, { bypassProjectLimit: true })).resolves.toMatchObject({ name: 'joined-room' });
    await expect(services.workspaceOps.getBrowserProjectLimitStatus()).resolves.toEqual({ current: 3, limit: 5, reached: false });
  });

  it('keeps Pro project creation unlimited', async () => {
    const testEnv = createTestEnvironment({ controller });
    const services = testEnv.instantiateAll();
    await testEnv.mountAll();
    vi.spyOn(services.projectEntitlements, 'projectLimit').mockResolvedValue(null);
    vi.spyOn(services.projectEntitlements, 'accountProjectIds').mockResolvedValue(new Set());

    for (let index = 1; index <= 7; index += 1) {
      await services.workspaceOps.createWorkspaceInfo({
        name: `pro-${index}`,
        type: WORKSPACE_STORAGE_TYPE.Browser,
        metadata: {},
      });
    }
    await expect(services.workspaceOps.getBrowserProjectLimitStatus()).resolves.toEqual({ current: 7, limit: null, reached: false });
  });

});
