import { describe, it, expect } from 'vitest';
import { serializeDNA, parseDNA, cloneDNA } from './serializer.js';
import type { CharacterDNA } from '../../types/character.js';

function makeDNA(overrides?: Partial<CharacterDNA>): CharacterDNA {
  return {
    id: 'test-uuid-1234',
    createdAt: '2025-01-01T00:00:00Z',
    updatedAt: '2025-01-01T00:00:00Z',
    status: 'active',
    identity: { name: 'Aylin', age: 24, nationality: 'TR', city: 'Istanbul', occupation: 'Model', personality_tags: ['fun', 'outgoing'], mbti: 'ENFP', backstory: 'A vibrant soul' },
    physicalMeasurements: { height_cm: 170, weight_kg: 55, body_type: 'slim', measurements: { bust: 90, waist: 60, hips: 90 } },
    skinMap: { base: '#F5D0A9', shadow: '#D4A574', highlight: '#FFE4C4' },
    faceAnatomy: { eye_shape: 'almond', eye_color: '#8B4513', eyebrow_shape: 'arched', nose_type: 'straight', lip_shape: 'full', cheekbones: 'high', jawline: 'soft', distinctive_marks: ['mole on left cheek'] },
    hairDNA: { style: 'wavy', length: 'long', texture: 'silky', root_color: '#3B2F2F', mid_color: '#5C4033', highlight_color: '#8B6914' },
    bodyBiometrics: { body_type_detail: 'hourglass', posture: 'upright', skin_texture: 'smooth' },
    profileView: { best_angle: 'left-45', signature_pose: 'hand-on-hip' },
    expressionMotion: { default_expression: 'smile', smile_type: 'warm', gaze_style: 'direct' },
    makeupStyle: { philosophy: 'natural', skin_finish: 'dewy', colors: { primary: '#C48793', secondary: '#E8B4B8', accent: '#8B0000' } },
    fashionStyle: { aesthetic: 'casual-chic', main_colors: ['#000000', '#FFFFFF', '#FF5733'], signature_pieces: ['jeans', 'blazer'] },
    technicalSettings: { camera_body: 'Canon R5', portrait_lens: '85mm f/1.4', full_body_lens: '35mm f/1.4', film_stock: 'Portra 400' },
    ...overrides,
  };
}

describe('serializeDNA', () => {
  it('should return a valid JSON string', () => {
    const dna = makeDNA();
    const json = serializeDNA(dna);
    expect(() => JSON.parse(json)).not.toThrow();
  });

  it('should preserve all 11 components', () => {
    const dna = makeDNA();
    const json = serializeDNA(dna);
    const parsed = JSON.parse(json);
    expect(parsed.identity).toBeDefined();
    expect(parsed.physicalMeasurements).toBeDefined();
    expect(parsed.skinMap).toBeDefined();
    expect(parsed.faceAnatomy).toBeDefined();
    expect(parsed.hairDNA).toBeDefined();
    expect(parsed.bodyBiometrics).toBeDefined();
    expect(parsed.profileView).toBeDefined();
    expect(parsed.expressionMotion).toBeDefined();
    expect(parsed.makeupStyle).toBeDefined();
    expect(parsed.fashionStyle).toBeDefined();
    expect(parsed.technicalSettings).toBeDefined();
  });

  it('should preserve HEX color codes', () => {
    const dna = makeDNA();
    const json = serializeDNA(dna);
    const parsed = JSON.parse(json);
    expect(parsed.skinMap.base).toBe('#F5D0A9');
    expect(parsed.hairDNA.root_color).toBe('#3B2F2F');
    expect(parsed.makeupStyle.colors.accent).toBe('#8B0000');
    expect(parsed.fashionStyle.main_colors).toEqual(['#000000', '#FFFFFF', '#FF5733']);
  });
});

describe('parseDNA', () => {
  it('should parse a valid JSON string into CharacterDNA', () => {
    const dna = makeDNA();
    const json = JSON.stringify(dna);
    const result = parseDNA(json);
    expect(result).toEqual(dna);
  });

  it('should throw on invalid JSON', () => {
    expect(() => parseDNA('not json')).toThrow('Invalid JSON');
  });

  it('should throw on non-object JSON', () => {
    expect(() => parseDNA('"just a string"')).toThrow('Invalid CharacterDNA');
  });

  it('should throw when required fields are missing', () => {
    expect(() => parseDNA('{}')).toThrow('Invalid CharacterDNA');
  });

  it('should throw on invalid HEX color', () => {
    const dna = makeDNA();
    dna.skinMap.base = 'not-hex';
    const json = JSON.stringify(dna);
    expect(() => parseDNA(json)).toThrow('Invalid CharacterDNA');
  });

  it('should throw on invalid MBTI', () => {
    const dna = makeDNA();
    (dna.identity as any).mbti = 'XXXX';
    const json = JSON.stringify(dna);
    expect(() => parseDNA(json)).toThrow('Invalid CharacterDNA');
  });

  it('should throw on invalid status', () => {
    const dna = makeDNA();
    (dna as any).status = 'unknown';
    const json = JSON.stringify(dna);
    expect(() => parseDNA(json)).toThrow('Invalid CharacterDNA');
  });
});

describe('round-trip consistency', () => {
  it('serialize(parse(serialize(dna))) === serialize(dna)', () => {
    const dna = makeDNA();
    const first = serializeDNA(dna);
    const reparsed = parseDNA(first);
    const second = serializeDNA(reparsed);
    expect(second).toBe(first);
  });

  it('preserves all data through round-trip', () => {
    const dna = makeDNA();
    const roundTripped = parseDNA(serializeDNA(dna));
    expect(roundTripped).toEqual(dna);
  });

  it('round-trip works with all status values', () => {
    for (const status of ['active', 'inactive', 'creating'] as const) {
      const dna = makeDNA({ status });
      const result = parseDNA(serializeDNA(dna));
      expect(result.status).toBe(status);
    }
  });
});

describe('cloneDNA', () => {
  it('should return an equal but independent copy', () => {
    const dna = makeDNA();
    const clone = cloneDNA(dna);
    expect(clone).toEqual(dna);
    // Verify independence
    clone.identity.name = 'Changed';
    expect(dna.identity.name).toBe('Aylin');
  });

  it('should deep clone arrays', () => {
    const dna = makeDNA();
    const clone = cloneDNA(dna);
    clone.fashionStyle.main_colors.push('#AABBCC');
    expect(dna.fashionStyle.main_colors).toHaveLength(3);
  });

  it('should deep clone nested objects', () => {
    const dna = makeDNA();
    const clone = cloneDNA(dna);
    clone.makeupStyle.colors.primary = '#000000';
    expect(dna.makeupStyle.colors.primary).toBe('#C48793');
  });
});
