/**
 * uat-plan/validate — structure + connection resolution against a real (temp) project.
 */
import { describe, it, expect, afterAll } from 'vitest';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { validate } from '../validate.js';

function makeProject(opts: { withConn?: boolean; withWeb?: boolean } = {}): string {
  const root = mkdtempSync(join(tmpdir(), 'uat-validate-'));
  const apiDir = join(root, 'src', 'Foo.Api');
  mkdirSync(apiDir, { recursive: true });
  writeFileSync(join(apiDir, 'Foo.Api.csproj'), '<Project Sdk="Microsoft.NET.Sdk"></Project>');
  if (opts.withConn !== false) {
    writeFileSync(
      join(apiDir, 'appsettings.Local.json'),
      JSON.stringify({ ConnectionStrings: { DefaultConnection: 'Server=(local);Database=TestDb;Integrated Security=true' } }),
    );
  }
  if (opts.withWeb !== false) {
    const ext = join(root, 'web', 'foo-web', 'src', 'extensions');
    mkdirSync(ext, { recursive: true });
    writeFileSync(join(root, 'web', 'foo-web', 'package.json'), JSON.stringify({ name: 'foo-web', dependencies: { react: '^18' } }));
    writeFileSync(join(ext, 'fooRegistry.ts'), `PageRegistry.register('administration.users', X);`);
  }
  return root;
}

describe('validate', () => {
  const created: string[] = [];
  const track = (r: string): string => {
    created.push(r);
    return r;
  };
  afterAll(() => {
    for (const r of created) rmSync(r, { recursive: true, force: true });
  });

  it('resolves api/web dirs, connection and derived name/outDir', async () => {
    const root = track(makeProject());
    const v = await validate({ projectPath: root, path: 'administration/users', includeApi: true });
    expect(v.valid).toBe(true);
    expect(v.layout?.apiDir.replace(/\\/g, '/')).toMatch(/src\/Foo\.Api$/);
    expect(v.layout?.webDir.replace(/\\/g, '/')).toMatch(/web\/foo-web$/);
    expect(v.layout?.name).toBe('users');
    expect(v.layout?.application).toBe('administration');
    expect(v.layout?.outDir).toBe('.application-test/uat/administration');
    expect(v.connection?.database).toBe('TestDb');
    expect(v.connectionSource).toBe('appsettings.Local.json');
  });

  it('honours an explicit name override and connectionString', async () => {
    const root = track(makeProject({ withConn: false }));
    const v = await validate({
      projectPath: root,
      path: 'administration/users',
      name: 'custom',
      includeApi: true,
      connectionString: 'Server=x;Database=Override;User Id=sa;Password=p',
    });
    expect(v.valid).toBe(true);
    expect(v.layout?.name).toBe('custom');
    expect(v.connectionSource).toBe('spec.connectionString');
    expect(v.connection?.database).toBe('Override');
  });

  it('warns (not fails) when the web app is absent', async () => {
    const root = track(makeProject({ withWeb: false }));
    const v = await validate({ projectPath: root, path: 'administration', includeApi: true });
    expect(v.valid).toBe(true);
    expect(v.layout?.webDir).toBe('');
    expect(v.warnings.some((w) => /Web app not located/.test(w))).toBe(true);
  });

  it('fails with a clear error when no connection can be resolved', async () => {
    const root = track(makeProject({ withConn: false }));
    const v = await validate({ projectPath: root, path: 'administration', includeApi: true });
    expect(v.valid).toBe(false);
    expect(v.errors.join(' ')).toMatch(/No SQL connection/);
  });

  it('rejects a schema-invalid spec', async () => {
    const v = await validate({ projectPath: '', path: '' });
    expect(v.valid).toBe(false);
  });

  it('fails when the API project is absent', async () => {
    const root = track(mkdtempSync(join(tmpdir(), 'uat-empty-')));
    const v = await validate({ projectPath: root, path: 'administration', includeApi: true });
    expect(v.valid).toBe(false);
    expect(v.errors.join(' ')).toMatch(/API project/);
  });
});
