import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
  signinSchema,
  signupSchema,
  profileUpdateSchema,
  passwordChangeSchema,
  createProjectSchema,
  updateProjectSchema,
  stepProgressSchema,
  migrationNotifyEnqueueSchema,
  updateCompanySchema,
} from './validators.js';

function ok(schema: { safeParse: (v: unknown) => { success: boolean } }, input: unknown, label = '') {
  const r = schema.safeParse(input);
  assert.equal(r.success, true, `expected ok for ${label || JSON.stringify(input)}`);
}

function fail(schema: { safeParse: (v: unknown) => { success: boolean } }, input: unknown, label = '') {
  const r = schema.safeParse(input);
  assert.equal(r.success, false, `expected fail for ${label || JSON.stringify(input)}`);
}

// ── signinSchema ──────────────────────────────────────────────────────────────

describe('signinSchema', () => {
  it('accepts valid email and password', () => {
    ok(signinSchema, { email: 'user@example.com', password: 'secret' });
  });

  it('rejects invalid email format', () => {
    fail(signinSchema, { email: 'not-an-email', password: 'secret' });
  });

  it('rejects empty password', () => {
    fail(signinSchema, { email: 'user@example.com', password: '' });
  });

  it('rejects missing fields', () => {
    fail(signinSchema, {});
    fail(signinSchema, { email: 'user@example.com' });
  });

  it('rejects email over 255 chars', () => {
    fail(signinSchema, { email: 'a'.repeat(250) + '@x.com', password: 'pw' });
  });

  it('rejects password over 128 chars', () => {
    fail(signinSchema, { email: 'user@example.com', password: 'a'.repeat(129) });
  });
});

// ── signupSchema ──────────────────────────────────────────────────────────────

describe('signupSchema', () => {
  it('accepts minimal valid signup', () => {
    ok(signupSchema, { email: 'user@example.com', password: 'secret123' });
  });

  it('accepts signup with optional name and company fields', () => {
    ok(signupSchema, {
      email: 'owner@corp.com',
      password: 'pass1234',
      name: 'Alice',
      createCompany: true,
      companyName: 'Acme',
      companyPackage: 'professional',
    });
  });

  it('rejects password shorter than 6 chars', () => {
    fail(signupSchema, { email: 'user@example.com', password: 'abc' });
  });

  it('rejects invalid company package', () => {
    fail(signupSchema, { email: 'user@example.com', password: 'pass123', companyPackage: 'free' });
  });

  it('defaults createCompany to false', () => {
    const r = signupSchema.safeParse({ email: 'u@x.com', password: 'pass123' });
    assert.equal(r.success, true);
    if (r.success) assert.equal(r.data.createCompany, false);
  });

  it('companyPackage is optional and undefined when omitted', () => {
    // .default('starter').optional() — the .optional() wraps .default(), so
    // undefined passes through without triggering the default at runtime.
    const r = signupSchema.safeParse({ email: 'u@x.com', password: 'pass123' });
    assert.equal(r.success, true);
    if (r.success) assert.equal(r.data.companyPackage, undefined);
  });
});

// ── profileUpdateSchema ───────────────────────────────────────────────────────

describe('profileUpdateSchema', () => {
  it('accepts update with only name', () => {
    ok(profileUpdateSchema, { name: 'Alice' });
  });

  it('accepts update with only email', () => {
    ok(profileUpdateSchema, { email: 'new@example.com' });
  });

  it('accepts update with both fields', () => {
    ok(profileUpdateSchema, { name: 'Alice', email: 'new@example.com' });
  });

  it('rejects empty object (needs at least one field)', () => {
    fail(profileUpdateSchema, {});
  });

  it('rejects name shorter than 2 chars', () => {
    fail(profileUpdateSchema, { name: 'A' });
  });

  it('rejects invalid email', () => {
    fail(profileUpdateSchema, { email: 'not-email' });
  });
});

// ── passwordChangeSchema ──────────────────────────────────────────────────────

describe('passwordChangeSchema', () => {
  it('accepts valid password change', () => {
    ok(passwordChangeSchema, { currentPassword: 'old-pass', newPassword: 'new-pass-123' });
  });

  it('rejects empty current password', () => {
    fail(passwordChangeSchema, { currentPassword: '', newPassword: 'new-pass-123' });
  });

  it('rejects new password shorter than 6 chars', () => {
    fail(passwordChangeSchema, { currentPassword: 'old', newPassword: 'abc' });
  });
});

// ── createProjectSchema ───────────────────────────────────────────────────────

describe('createProjectSchema', () => {
  const BASE = {
    id: 'proj-1',
    name: 'My Project',
    description: 'Migration project',
    sourceLanguage: 'cobol',
    targetLanguage: 'java',
  };

  it('accepts minimal valid project', () => {
    ok(createProjectSchema, BASE);
  });

  it('accepts project with all optional fields', () => {
    ok(createProjectSchema, {
      ...BASE,
      repoUrl: 'https://github.com/org/repo',
      tags: ['banking', 'cobol'],
      team: [{ name: 'Alice', role: 'Lead', avatar: '🧑' }],
      status: 'draft',
      config: { qualityLevel: 2 },
    });
  });

  it('accepts empty string for repoUrl', () => {
    ok(createProjectSchema, { ...BASE, repoUrl: '' });
  });

  it('rejects missing required fields', () => {
    fail(createProjectSchema, { name: 'x', description: 'y', sourceLanguage: 'cobol' });
  });

  it('rejects empty name', () => {
    fail(createProjectSchema, { ...BASE, name: '' });
  });

  it('rejects invalid status', () => {
    fail(createProjectSchema, { ...BASE, status: 'unknown-status' });
  });

  it('rejects invalid repoUrl', () => {
    fail(createProjectSchema, { ...BASE, repoUrl: 'not-a-url' });
  });

  it('accepts all valid status values', () => {
    for (const status of ['draft', 'analyzing', 'converting', 'validating', 'completed', 'failed']) {
      ok(createProjectSchema, { ...BASE, status }, status);
    }
  });
});

// ── updateProjectSchema ───────────────────────────────────────────────────────

describe('updateProjectSchema', () => {
  it('accepts single field update', () => {
    ok(updateProjectSchema, { status: 'completed' });
    ok(updateProjectSchema, { maxReachedStep: 3 });
    ok(updateProjectSchema, { totalFiles: 47 });
  });

  it('rejects empty object', () => {
    fail(updateProjectSchema, {});
  });

  it('rejects maxReachedStep out of range', () => {
    fail(updateProjectSchema, { maxReachedStep: -1 });
    fail(updateProjectSchema, { maxReachedStep: 11 });
  });

  it('rejects accuracy out of 0-100 range', () => {
    fail(updateProjectSchema, { accuracy: 101 });
    fail(updateProjectSchema, { accuracy: -1 });
  });

  it('accepts accuracy at boundaries', () => {
    ok(updateProjectSchema, { accuracy: 0 });
    ok(updateProjectSchema, { accuracy: 100 });
  });

  it('rejects invalid status', () => {
    fail(updateProjectSchema, { status: 'running' });
  });
});

// ── stepProgressSchema ────────────────────────────────────────────────────────

describe('stepProgressSchema', () => {
  it('accepts valid step progress', () => {
    ok(stepProgressSchema, { stepNumber: 2, status: 'completed' });
  });

  it('accepts all valid status values', () => {
    for (const status of ['pending', 'in_progress', 'completed', 'failed']) {
      ok(stepProgressSchema, { stepNumber: 1, status }, status);
    }
  });

  it('rejects invalid status', () => {
    fail(stepProgressSchema, { stepNumber: 1, status: 'skipped' });
  });

  it('rejects stepNumber out of 0-8 range', () => {
    fail(stepProgressSchema, { stepNumber: -1, status: 'pending' });
    fail(stepProgressSchema, { stepNumber: 9, status: 'pending' });
  });

  it('accepts boundary step numbers', () => {
    ok(stepProgressSchema, { stepNumber: 0, status: 'pending' });
    ok(stepProgressSchema, { stepNumber: 8, status: 'completed' });
  });

  it('accepts optional maxReachedStep and metadata', () => {
    ok(stepProgressSchema, { stepNumber: 3, status: 'completed', maxReachedStep: 5, metadata: { key: 'val' } });
  });
});

// ── migrationNotifyEnqueueSchema ──────────────────────────────────────────────

describe('migrationNotifyEnqueueSchema', () => {
  it('accepts valid event types', () => {
    for (const event of ['complete', 'warning', 'failure']) {
      ok(migrationNotifyEnqueueSchema, { event }, event);
    }
  });

  it('accepts optional detail', () => {
    ok(migrationNotifyEnqueueSchema, { event: 'complete', detail: 'Migration done in 3m' });
  });

  it('rejects unknown event type', () => {
    fail(migrationNotifyEnqueueSchema, { event: 'started' });
  });

  it('rejects missing event', () => {
    fail(migrationNotifyEnqueueSchema, {});
    fail(migrationNotifyEnqueueSchema, { detail: 'some detail' });
  });

  it('rejects detail over 4000 chars', () => {
    fail(migrationNotifyEnqueueSchema, { event: 'complete', detail: 'x'.repeat(4001) });
  });
});

// ── updateCompanySchema ───────────────────────────────────────────────────────

describe('updateCompanySchema', () => {
  it('accepts single field update', () => {
    ok(updateCompanySchema, { name: 'New Corp' });
    ok(updateCompanySchema, { package: 'enterprise' });
    ok(updateCompanySchema, { maxUsers: 50 });
  });

  it('rejects empty object', () => {
    fail(updateCompanySchema, {});
  });

  it('rejects invalid package', () => {
    fail(updateCompanySchema, { package: 'free' });
  });

  it('rejects maxUsers below 1', () => {
    fail(updateCompanySchema, { maxUsers: 0 });
  });

  it('rejects maxUsers above 1000', () => {
    fail(updateCompanySchema, { maxUsers: 1001 });
  });
});
