import { describe, expect, it, vi } from 'vitest';
import { noModuleAccess } from '../../common/access/modules';
import type { AuthenticatedUser } from '../../common/auth/authenticated-user';
import type { AccountSharingRepository } from './account-sharing.repository';
import { AccountSharingService } from './account-sharing.service';
import type { ModuleAccessRepository } from '../../common/access/module-access.repository';

const admin = {
  id: '11111111-1111-4111-8111-111111111111',
  email: 'admin@example.test',
  displayName: 'Admin',
  role: 'ADMIN' as const,
  isActive: true,
};
const customer = {
  id: '22222222-2222-4222-8222-222222222222',
  email: 'customer@example.test',
  displayName: 'Customer',
  role: 'USER' as const,
  isActive: true,
};
const actor: AuthenticatedUser = {
  ...admin,
  isReadOnly: false,
  subscriptionTier: null,
  accessExpiresAt: null,
  sessionId: 'session',
  modules: {
    trading: 'WRITE', portfolio: 'WRITE', market: 'WRITE',
    capital: 'WRITE', management: 'WRITE', community: 'WRITE',
  },
};

function createService(overrides?: {
  shares?: Array<Record<string, unknown>>;
  states?: Array<Record<string, unknown>>;
}) {
  const repository = {
    listUsers: vi.fn().mockResolvedValue([admin, customer]),
    listUsersByIds: vi.fn().mockResolvedValue([admin]),
    listSourceStates: vi.fn().mockResolvedValue(overrides?.states ?? [{
      ownerId: admin.id,
      key: 'ck_trading_terminal_v10_30_live',
      value: JSON.stringify({ accounts: [
        { id: 'fm', name: 'F&M', trades: [{ id: 'one' }] },
        { id: 'private', name: 'Private', trades: [{ id: 'two' }] },
      ] }),
    }]),
    listSourceStatesFor: vi.fn().mockResolvedValue(overrides?.states ?? [{
      ownerId: admin.id,
      key: 'ck_trading_terminal_v10_30_live',
      value: JSON.stringify({ accounts: [
        { id: 'fm', name: 'F&M', trades: [{ id: 'one' }] },
        { id: 'private', name: 'Private', trades: [{ id: 'two' }] },
      ] }),
    }]),
    listShares: vi.fn().mockResolvedValue([]),
    listSharesFor: vi.fn().mockResolvedValue(overrides?.shares ?? []),
    create: vi.fn().mockResolvedValue({ id: '33333333-3333-4333-8333-333333333333' }),
    delete: vi.fn().mockResolvedValue(true),
  };
  const moduleAccess = {
    forUser: vi.fn().mockResolvedValue(noModuleAccess()),
    set: vi.fn().mockResolvedValue(undefined),
  };
  const service = new AccountSharingService(
    repository as unknown as AccountSharingRepository,
    moduleAccess as unknown as ModuleAccessRepository,
  );
  return { service, repository, moduleAccess };
}

describe('AccountSharingService', () => {
  it('returns only the specifically granted source account as a read-only snapshot', async () => {
    const share = {
      id: '33333333-3333-4333-8333-333333333333',
      module: 'trading',
      sourceOwnerId: admin.id,
      sourceAccountId: 'fm',
      granteeId: customer.id,
    };
    const { service } = createService({ shares: [share] });

    const result = await service.listFor(customer.id, 'trading');

    expect(result).toHaveLength(1);
    expect(result[0]).toMatchObject({
      id: `shared:${share.id}`,
      name: 'F&M',
      sharedAccess: {
        sourceAccountId: 'fm',
        sourceOwnerId: admin.id,
        readOnly: true,
      },
    });
    expect(result.some((account) => account.name === 'Private')).toBe(false);
  });

  it('automatically enables READ when an admin grants a managed account', async () => {
    const { service, moduleAccess } = createService();

    await service.create({
      module: 'trading',
      sourceOwnerId: admin.id,
      sourceAccountId: 'fm',
      granteeId: customer.id,
    }, actor);

    expect(moduleAccess.set).toHaveBeenCalledWith(customer.id, { trading: 'READ' });
  });

  it('stops exposing a share when its managing administrator is deactivated', async () => {
    const { service, repository } = createService({
      shares: [{
        id: '33333333-3333-4333-8333-333333333333',
        module: 'trading',
        sourceOwnerId: admin.id,
        sourceAccountId: 'fm',
        granteeId: customer.id,
      }],
    });
    repository.listUsersByIds.mockResolvedValue([{ ...admin, isActive: false }]);

    await expect(service.listFor(customer.id, 'trading')).resolves.toEqual([]);
  });
});
