import { describe, expect, it } from 'vitest';
import path from 'node:path';
import { migrationsOutputDir, buildMigrationsAddArgs } from '../ef-runner.js';

// Inputs are built with path.join so the assertion is identical on Windows and
// POSIX runners (path.relative uses the platform separator; the helper must
// always normalise the result back to forward slashes).

describe('migrationsOutputDir — folder EF must write the migration into', () => {
  it('returns the SmartStack Persistence/Migrations convention, relative to the project dir', () => {
    const proj = path.join('repo', 'src', 'App.Infrastructure');
    const csproj = path.join(proj, 'App.Infrastructure.csproj');
    const migDir = path.join(proj, 'Persistence', 'Migrations');
    expect(migrationsOutputDir(csproj, migDir)).toBe('Persistence/Migrations');
  });

  it('returns the bare default "Migrations" when the repo uses the default layout (no-op fix)', () => {
    const proj = path.join('repo', 'src', 'App.Infrastructure');
    const csproj = path.join(proj, 'App.Infrastructure.csproj');
    const migDir = path.join(proj, 'Migrations');
    expect(migrationsOutputDir(csproj, migDir)).toBe('Migrations');
  });

  it('handles deeper nesting and always emits forward slashes', () => {
    const proj = path.join('a', 'b', 'My.Infra');
    const csproj = path.join(proj, 'My.Infra.csproj');
    const migDir = path.join(proj, 'Data', 'EF', 'Migrations');
    expect(migrationsOutputDir(csproj, migDir)).toBe('Data/EF/Migrations');
  });

  it('works with absolute paths (detection returns absolute paths)', () => {
    const proj = path.resolve(path.join('tmp', 'src', 'App.Infrastructure'));
    const csproj = path.join(proj, 'App.Infrastructure.csproj');
    const migDir = path.join(proj, 'Persistence', 'Migrations');
    expect(migrationsOutputDir(csproj, migDir)).toBe('Persistence/Migrations');
  });
});

describe('buildMigrationsAddArgs — single source of truth for `dotnet ef migrations add`', () => {
  const proj = path.join('repo', 'src', 'App.Infrastructure');
  const csproj = path.join(proj, 'App.Infrastructure.csproj');
  const migDir = path.join(proj, 'Persistence', 'Migrations');

  it('ALWAYS passes --output-dir = migrationsOutputDir (the regression that mis-placed the squash migration)', () => {
    const args = buildMigrationsAddArgs({
      migrationName: 'ext_v0_1_0_001_GafConsolidated',
      csprojPath: csproj,
      contextName: 'ExtensionsDbContext',
      migrationsDir: migDir,
    });
    expect(args).toContain('--output-dir');
    const i = args.indexOf('--output-dir');
    expect(args[i + 1]).toBe(migrationsOutputDir(csproj, migDir));
    expect(args[i + 1]).toBe('Persistence/Migrations');
  });

  it('carries the migration name, project, context and --no-color', () => {
    const args = buildMigrationsAddArgs({
      migrationName: 'core_v1_0_0_002_AddX',
      csprojPath: csproj,
      contextName: 'CoreDbContext',
      migrationsDir: migDir,
    });
    expect(args.slice(0, 4)).toEqual(['ef', 'migrations', 'add', 'core_v1_0_0_002_AddX']);
    expect(args[args.indexOf('--project') + 1]).toBe(csproj);
    expect(args[args.indexOf('--context') + 1]).toBe('CoreDbContext');
    expect(args).toContain('--no-color');
  });

  it('appends --startup-project only when provided', () => {
    const withSp = buildMigrationsAddArgs({
      migrationName: 'm',
      csprojPath: csproj,
      contextName: 'C',
      migrationsDir: migDir,
      startupProject: path.join('src', 'App.Api', 'App.Api.csproj'),
    });
    expect(withSp).toContain('--startup-project');
    expect(withSp[withSp.indexOf('--startup-project') + 1]).toBe(path.join('src', 'App.Api', 'App.Api.csproj'));

    const without = buildMigrationsAddArgs({
      migrationName: 'm',
      csprojPath: csproj,
      contextName: 'C',
      migrationsDir: migDir,
    });
    expect(without).not.toContain('--startup-project');
  });
});
