import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { afterEach, describe, expect, it } from 'vitest';
import {
  SHIPPED_FLEET_ARTIFACT_DISPOSITIONS,
  validateShippedFleetArtifactDispositions,
} from './example-profile-dispositions.js';

const frameworkFleet = resolve(
  dirname(fileURLToPath(import.meta.url)),
  '..',
  '..',
  'framework',
  'fleet',
);

const EXPECTED_DISPOSITIONS = [
  'examples/coding.yaml:v1-fixture',
  'examples/general.yaml:v1-fixture',
  'examples/hybrid.yaml:v1-fixture',
  'examples/local-canary.yaml:v1-fixture',
  'examples/minimal.yaml:v1-fixture',
  'examples/operator-interaction.yaml:v1-fixture',
  'examples/research.yaml:v1-fixture',
  'profiles/business.yaml:canonical-profile',
  'profiles/marketing.yaml:canonical-profile',
  'profiles/personal-assistant.yaml:canonical-profile',
  'profiles/research.yaml:canonical-profile',
  'profiles/software-delivery.yaml:canonical-profile',
  'services/operator-interaction.yaml:canonical-service-policy',
];

const declared = SHIPPED_FLEET_ARTIFACT_DISPOSITIONS.map(
  ({ path, disposition }): string => `${path}:${disposition}`,
);

describe('shipped fleet example/profile/service disposition validation', (): void => {
  it('enumerates every inventory artifact with an explicit executable disposition', (): void => {
    expect(declared).toEqual(EXPECTED_DISPOSITIONS);
  });

  it('validates every shipped artifact through its declared v1 or canonical path', async (): Promise<void> => {
    const results = await validateShippedFleetArtifactDispositions({ frameworkFleet });

    expect(results.map(({ path, disposition }): string => `${path}:${disposition}`)).toEqual(
      EXPECTED_DISPOSITIONS,
    );
    expect(results.filter(({ disposition }): boolean => disposition === 'v1-fixture')).toHaveLength(
      7,
    );
    expect(
      results.filter(({ disposition }): boolean => disposition === 'canonical-profile'),
    ).toHaveLength(5);
    expect(
      results.find(({ path }): boolean => path === 'services/operator-interaction.yaml'),
    ).toMatchObject({
      disposition: 'canonical-service-policy',
    });
  });

  let temporaryFleet: string | undefined;
  afterEach(async (): Promise<void> => {
    if (temporaryFleet) await rm(temporaryFleet, { recursive: true, force: true });
    temporaryFleet = undefined;
  });

  it('fails closed when a shipped artifact lacks a declared disposition', async (): Promise<void> => {
    temporaryFleet = await mkdtemp(join(tmpdir(), 'mosaic-dispositions-'));
    await cp(frameworkFleet, temporaryFleet, { recursive: true });
    await writeFile(join(temporaryFleet, 'examples', 'undeclared.yaml'), 'version: 1\n');

    await expect(
      validateShippedFleetArtifactDispositions({ frameworkFleet: temporaryFleet }),
    ).rejects.toThrow(/undeclared shipped fleet artifact.*examples\/undeclared.yaml/i);
  });

  it('rejects a v1 fixture when its explicit version declaration is removed', async (): Promise<void> => {
    temporaryFleet = await mkdtemp(join(tmpdir(), 'mosaic-dispositions-'));
    await cp(frameworkFleet, temporaryFleet, { recursive: true });
    const fixturePath = join(temporaryFleet, 'examples', 'coding.yaml');
    const fixture = await readFile(fixturePath, 'utf8');
    await writeFile(fixturePath, fixture.replace(/^version: 1\n/, ''));

    await expect(
      validateShippedFleetArtifactDispositions({ frameworkFleet: temporaryFleet }),
    ).rejects.toThrow(/Fleet roster version must be 1/);
  });
});
