// Feature: client-portal, Property 5: Mock data determinism
// Feature: client-portal, Property 6: Mock data entity completeness
// **Validates: Requirements 15.1, 15.2, 15.4, 15.5, 15.6, 15.7, 15.8**

import { describe, it, expect } from 'vitest';
import * as fc from 'fast-check';
import { createRng } from '@/lib/mock-data/seed';
import { generateClientPortalData } from '@/lib/mock-data/generators/client-portal';
import type {
  PortalPeriod,
  FinancialTimePoint,
  PortalTopSong,
  PortalIncomeGroup,
  PortalExploitationSource,
  PortalTerritory,
  PortalStatement,
  PortalDocument,
  PortalSong,
} from '@/lib/mock-data/types';

/**
 * Deep equality check that handles Date objects by comparing their time values.
 */
function deepEqual(a: unknown, b: unknown): boolean {
  if (a instanceof Date && b instanceof Date) {
    return a.getTime() === b.getTime();
  }
  if (a === b) return true;
  if (a === null || b === null) return a === b;
  if (typeof a !== typeof b) return false;
  if (Array.isArray(a) && Array.isArray(b)) {
    if (a.length !== b.length) return false;
    return a.every((val, i) => deepEqual(val, b[i]));
  }
  if (typeof a === 'object' && typeof b === 'object') {
    const aObj = a as Record<string, unknown>;
    const bObj = b as Record<string, unknown>;
    const aKeys = Object.keys(aObj);
    const bKeys = Object.keys(bObj);
    if (aKeys.length !== bKeys.length) return false;
    return aKeys.every((key) => deepEqual(aObj[key], bObj[key]));
  }
  return false;
}

describe('Property 5: Mock data determinism', () => {
  it('calling the generator twice with the same seed produces identical output', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng1 = createRng(seed);
        const rng2 = createRng(seed);

        const data1 = generateClientPortalData(rng1);
        const data2 = generateClientPortalData(rng2);

        // Verify all collections are deeply equal
        expect(deepEqual(data1.periods, data2.periods)).toBe(true);
        expect(deepEqual(data1.financialTimeSeries, data2.financialTimeSeries)).toBe(true);
        expect(deepEqual(data1.topSongs, data2.topSongs)).toBe(true);
        expect(deepEqual(data1.incomeGroups, data2.incomeGroups)).toBe(true);
        expect(deepEqual(data1.exploitationSources, data2.exploitationSources)).toBe(true);
        expect(deepEqual(data1.territories, data2.territories)).toBe(true);
        expect(deepEqual(data1.statements, data2.statements)).toBe(true);
        expect(deepEqual(data1.documents, data2.documents)).toBe(true);
        expect(deepEqual(data1.songs, data2.songs)).toBe(true);
      }),
      { numRuns: 100 },
    );
  });
});

describe('Property 6: Mock data entity completeness', () => {
  it('all PortalPeriod fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const period of data.periods) {
          const p = period as PortalPeriod;
          expect(typeof p.id).toBe('string');
          expect(p.id.length).toBeGreaterThan(0);
          expect(typeof p.label).toBe('string');
          expect(p.label.length).toBeGreaterThan(0);
          expect(p.startDate).toBeInstanceOf(Date);
          expect(p.endDate).toBeInstanceOf(Date);
          expect(['Activo', 'Establecido', 'Pendiente']).toContain(p.status);
          expect(typeof p.balance).toBe('number');
          expect(typeof p.advances).toBe('number');
          expect(typeof p.adjustments).toBe('number');
          expect(typeof p.netEarnings).toBe('number');
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all FinancialTimePoint fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        expect(data.financialTimeSeries.length).toBeGreaterThanOrEqual(18);

        for (const point of data.financialTimeSeries) {
          const p = point as FinancialTimePoint;
          expect(typeof p.month).toBe('string');
          expect(p.month.length).toBeGreaterThan(0);
          expect(typeof p.netEarnings).toBe('number');
          expect(typeof p.finalBalance).toBe('number');
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalTopSong fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const song of data.topSongs) {
          const s = song as PortalTopSong;
          expect(typeof s.id).toBe('string');
          expect(s.id.length).toBeGreaterThan(0);
          expect(typeof s.title).toBe('string');
          expect(s.title.length).toBeGreaterThan(0);
          expect(Array.isArray(s.composers)).toBe(true);
          expect(s.composers.length).toBeGreaterThanOrEqual(1);
          for (const c of s.composers) {
            expect(typeof c).toBe('string');
            expect(c.length).toBeGreaterThan(0);
          }
          expect(typeof s.income).toBe('number');
          expect(s.income).toBeGreaterThan(0);
          expect(typeof s.percentage).toBe('number');
          expect(s.percentage).toBeGreaterThanOrEqual(0);
          expect(s.percentage).toBeLessThanOrEqual(100);
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalIncomeGroup fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const group of data.incomeGroups) {
          const g = group as PortalIncomeGroup;
          expect(typeof g.category).toBe('string');
          expect(g.category.length).toBeGreaterThan(0);
          expect(typeof g.amount).toBe('number');
          expect(g.amount).toBeGreaterThan(0);
          expect(typeof g.percentage).toBe('number');
          expect(g.percentage).toBeGreaterThanOrEqual(0);
          expect(g.percentage).toBeLessThanOrEqual(100);
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalExploitationSource fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const source of data.exploitationSources) {
          const s = source as PortalExploitationSource;
          expect(typeof s.platform).toBe('string');
          expect(s.platform.length).toBeGreaterThan(0);
          expect(typeof s.amount).toBe('number');
          expect(s.amount).toBeGreaterThan(0);
          expect(typeof s.percentage).toBe('number');
          expect(s.percentage).toBeGreaterThanOrEqual(0);
          expect(s.percentage).toBeLessThanOrEqual(100);
          expect(typeof s.trend).toBe('number');
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalTerritory fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const territory of data.territories) {
          const t = territory as PortalTerritory;
          expect(typeof t.country).toBe('string');
          expect(t.country.length).toBeGreaterThan(0);
          expect(typeof t.amount).toBe('number');
          expect(t.amount).toBeGreaterThan(0);
          expect(typeof t.percentage).toBe('number');
          expect(t.percentage).toBeGreaterThanOrEqual(0);
          expect(t.percentage).toBeLessThanOrEqual(100);
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalStatement fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const stmt of data.statements) {
          const s = stmt as PortalStatement;
          expect(typeof s.id).toBe('string');
          expect(s.id.length).toBeGreaterThan(0);
          expect(typeof s.periodLabel).toBe('string');
          expect(s.periodLabel.length).toBeGreaterThan(0);
          expect(typeof s.downloadUrl).toBe('string');
          expect(s.downloadUrl.length).toBeGreaterThan(0);
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalDocument fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const doc of data.documents) {
          const d = doc as PortalDocument;
          expect(typeof d.id).toBe('string');
          expect(d.id.length).toBeGreaterThan(0);
          expect(typeof d.name).toBe('string');
          expect(d.name.length).toBeGreaterThan(0);
          expect(['statement', 'contract', 'report']).toContain(d.type);
          expect(typeof d.downloadUrl).toBe('string');
          expect(d.downloadUrl.length).toBeGreaterThan(0);
          // periodLabel is optional but if present must be a non-empty string
          if (d.periodLabel !== undefined) {
            expect(typeof d.periodLabel).toBe('string');
            expect(d.periodLabel.length).toBeGreaterThan(0);
          }
        }
      }),
      { numRuns: 100 },
    );
  });

  it('all PortalSong fields are present and non-null', () => {
    fc.assert(
      fc.property(fc.integer({ min: 1, max: 1000000 }), (seed) => {
        const rng = createRng(seed);
        const data = generateClientPortalData(rng);

        for (const song of data.songs) {
          const s = song as PortalSong;
          expect(typeof s.id).toBe('string');
          expect(s.id.length).toBeGreaterThan(0);
          expect(typeof s.title).toBe('string');
          expect(s.title.length).toBeGreaterThan(0);
          expect(Array.isArray(s.composers)).toBe(true);
          expect(s.composers.length).toBeGreaterThanOrEqual(1);
          for (const c of s.composers) {
            expect(typeof c).toBe('string');
            expect(c.length).toBeGreaterThan(0);
          }
          expect(typeof s.iswc).toBe('string');
          expect(s.iswc.length).toBeGreaterThan(0);
          expect(s.registrationDate).toBeInstanceOf(Date);
        }
      }),
      { numRuns: 100 },
    );
  });
});
