// Feature: divisi-metrics, Property 1: Generated entity structural validity
// **Validates: Requirements 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10**

import { describe, it, expect } from 'vitest';
import * as fc from 'fast-check';
import {
  works,
  composers,
  payees,
  contracts,
  income,
  transactions,
  costs,
  statements,
  suspenseItems,
  auditEvents,
} from '@/lib/mock-data';
import type {
  Work,
  Composer,
  Payee,
  Contract,
  IncomeRecord,
  Transaction,
  Cost,
  Statement,
  SuspenseItem,
  AuditEvent,
} from '@/lib/mock-data/types';

/**
 * Entity metadata: maps an index (0–9) to the collection, its minimum
 * required size, a human-readable label, and a structural validator.
 */
const entityConfigs = [
  {
    label: 'Work',
    collection: works,
    minCount: 200,
    validate: (r: Work) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(typeof r.title).toBe('string');
      expect(r.title.length).toBeGreaterThan(0);
      expect(typeof r.iswc).toBe('string');
      expect(r.iswc.length).toBeGreaterThan(0);
      expect(typeof r.genre).toBe('string');
      expect(r.genre.length).toBeGreaterThan(0);
      expect(['controlled', 'uncontrolled']).toContain(r.rightsType);
      expect(Array.isArray(r.writerSplits)).toBe(true);
      expect(r.writerSplits.length).toBeGreaterThanOrEqual(1);
      for (const split of r.writerSplits) {
        expect(typeof split.composerId).toBe('string');
        expect(typeof split.percentage).toBe('number');
      }
      expect(r.createdAt).toBeInstanceOf(Date);
      expect(Array.isArray(r.monthlyIncome)).toBe(true);
      expect(r.monthlyIncome.length).toBeGreaterThanOrEqual(1);
      for (const mi of r.monthlyIncome) {
        expect(typeof mi.month).toBe('string');
        expect(typeof mi.amount).toBe('number');
      }
    },
  },
  {
    label: 'Composer',
    collection: composers,
    minCount: 50,
    validate: (r: Composer) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(typeof r.name).toBe('string');
      expect(r.name.length).toBeGreaterThan(0);
      expect(typeof r.workCount).toBe('number');
      expect(r.workCount).toBeGreaterThanOrEqual(0);
      expect(Array.isArray(r.associatedWorkIds)).toBe(true);
      expect(Array.isArray(r.monthlyIncome)).toBe(true);
      expect(r.monthlyIncome.length).toBeGreaterThanOrEqual(1);
      expect(r.createdAt).toBeInstanceOf(Date);
    },
  },
  {
    label: 'Payee',
    collection: payees,
    minCount: 40,
    validate: (r: Payee) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(typeof r.name).toBe('string');
      expect(r.name.length).toBeGreaterThan(0);
      expect(['publisher', 'sub-publisher', 'administrator', 'individual']).toContain(r.type);
      expect(typeof r.territory).toBe('string');
      expect(r.territory.length).toBeGreaterThan(0);
      expect(typeof r.cumulativeEarnings).toBe('number');
    },
  },
  {
    label: 'Contract',
    collection: contracts,
    minCount: 60,
    validate: (r: Contract) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(typeof r.territory).toBe('string');
      expect(r.territory.length).toBeGreaterThan(0);
      expect(typeof r.currency).toBe('string');
      expect(r.currency.length).toBeGreaterThan(0);
      expect(typeof r.royaltySplit).toBe('number');
      expect(r.startDate).toBeInstanceOf(Date);
      expect(r.endDate).toBeInstanceOf(Date);
      expect(['active', 'inactive']).toContain(r.status);
    },
  },
  {
    label: 'IncomeRecord',
    collection: income,
    minCount: 500,
    validate: (r: IncomeRecord) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(typeof r.workId).toBe('string');
      expect(r.workId.length).toBeGreaterThan(0);
      expect(typeof r.amount).toBe('number');
      expect(typeof r.grossAmount).toBe('number');
      expect(typeof r.netAmount).toBe('number');
      expect(typeof r.territory).toBe('string');
      expect(r.territory.length).toBeGreaterThan(0);
      expect(typeof r.mediaType).toBe('string');
      expect(r.mediaType.length).toBeGreaterThan(0);
      expect(['PRO', 'DSP', 'sub-publisher']).toContain(r.source);
      expect(r.date).toBeInstanceOf(Date);
    },
  },
  {
    label: 'Transaction',
    collection: transactions,
    minCount: 300,
    validate: (r: Transaction) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(['royalty', 'fee', 'advance', 'adjustment']).toContain(r.type);
      expect(typeof r.amount).toBe('number');
      expect(typeof r.currency).toBe('string');
      expect(r.currency.length).toBeGreaterThan(0);
      expect(r.date).toBeInstanceOf(Date);
    },
  },
  {
    label: 'Cost',
    collection: costs,
    minCount: 100,
    validate: (r: Cost) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(['administration', 'ownership-purchase', 'other']).toContain(r.type);
      expect(typeof r.amount).toBe('number');
      expect(typeof r.currency).toBe('string');
      expect(r.currency.length).toBeGreaterThan(0);
      expect(r.date).toBeInstanceOf(Date);
    },
  },
  {
    label: 'Statement',
    collection: statements,
    minCount: 30,
    validate: (r: Statement) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(typeof r.period).toBe('string');
      expect(r.period.length).toBeGreaterThan(0);
      expect(typeof r.payeeId).toBe('string');
      expect(r.payeeId.length).toBeGreaterThan(0);
      expect(typeof r.payeeName).toBe('string');
      expect(r.payeeName.length).toBeGreaterThan(0);
      expect(typeof r.currency).toBe('string');
      expect(r.currency.length).toBeGreaterThan(0);
      expect(['Draft', 'Final', 'Published']).toContain(r.status);
      expect(typeof r.grossAmount).toBe('number');
      expect(typeof r.deductions).toBe('number');
      expect(typeof r.netPayable).toBe('number');
    },
  },
  {
    label: 'SuspenseItem',
    collection: suspenseItems,
    minCount: 40,
    validate: (r: SuspenseItem) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(r.date).toBeInstanceOf(Date);
      expect(typeof r.sourceFile).toBe('string');
      expect(r.sourceFile.length).toBeGreaterThan(0);
      expect(['unmatched', 'ambiguous']).toContain(r.matchType);
      expect(['pending', 'resolved']).toContain(r.resolutionStatus);
      // suggestedWorkId is string | null
      expect(r.suggestedWorkId === null || typeof r.suggestedWorkId === 'string').toBe(true);
    },
  },
  {
    label: 'AuditEvent',
    collection: auditEvents,
    minCount: 100,
    validate: (r: AuditEvent) => {
      expect(typeof r.id).toBe('string');
      expect(r.id.length).toBeGreaterThan(0);
      expect(r.timestamp).toBeInstanceOf(Date);
      expect(typeof r.entityType).toBe('string');
      expect(r.entityType.length).toBeGreaterThan(0);
      expect(['create', 'update', 'delete']).toContain(r.actionType);
      expect(typeof r.userName).toBe('string');
      expect(r.userName.length).toBeGreaterThan(0);
    },
  },
] as const;

describe('Property 1: Generated entity structural validity', () => {
  it('every generated record has required fields with correct types', () => {
    fc.assert(
      fc.property(fc.integer({ min: 0, max: 9 }), (entityIndex) => {
        const config = entityConfigs[entityIndex];
        const collection = config.collection as unknown[];

        // Validate every record in the collection
        for (const record of collection) {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          (config.validate as (r: any) => void)(record);
        }
      }),
      { numRuns: 100 },
    );
  });

  it('each collection meets or exceeds the minimum required size', () => {
    fc.assert(
      fc.property(fc.integer({ min: 0, max: 9 }), (entityIndex) => {
        const config = entityConfigs[entityIndex];
        const collection = config.collection as unknown[];
        expect(collection.length).toBeGreaterThanOrEqual(config.minCount);
      }),
      { numRuns: 100 },
    );
  });
});
