/**
 * Character Service Tests
 * Tests the character business logic with mock implementations
 */
import { describe, it, expect, beforeEach } from 'vitest';

// Mock name data
const MALE_NAMES = ['John', 'James', 'Michael', 'William', 'David', 'Richard', 'Joseph', 'Thomas', 'Charles', 'Daniel', 'Matthew', 'Anthony', 'Mark', 'Steven', 'Paul'];
const FEMALE_NAMES = ['Mary', 'Patricia', 'Jennifer', 'Linda', 'Elizabeth', 'Barbara', 'Susan', 'Jessica', 'Sarah', 'Karen', 'Nancy', 'Lisa', 'Betty', 'Margaret', 'Sandra'];
const LAST_NAMES = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez', 'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson'];

const AVAILABLE_TRAITS = ['Outgoing', 'Creative', 'Ambitious', 'Kind', 'Shy', 'Stubborn', 'Adventurous', 'Intellectual', 'Athletic', 'Artistic', 'Loyal', 'Independent', 'Perfectionist', 'Optimistic', 'Practical'];
const AVAILABLE_INTERESTS = ['Music', 'Sports', 'Reading', 'Movies', 'Cooking', 'Travel', 'Photography', 'Gaming', 'Art', 'Science', 'Technology', 'Fashion', 'Dance', 'Nature', 'Writing'];

// Mock character type
interface MockCharacter {
  id: string;
  firstname: string;
  lastname: string;
  sex: 'Male' | 'Female';
  ageYears: number;
  ageDays: number;
  status: string;
  health: number;
  happiness: number;
  intelligence: number;
  money: number;
  diamonds: number;
  traits: string[];
  interests: string[];
}

// Mock service functions
function generateRandomName(sex: string): { firstname: string; lastname: string } {
  const firstNames = sex === 'male' ? MALE_NAMES : FEMALE_NAMES;
  const firstname = firstNames[Math.floor(Math.random() * firstNames.length)];
  const lastname = LAST_NAMES[Math.floor(Math.random() * LAST_NAMES.length)];
  return { firstname, lastname };
}

function generateRandomTraits(count: number = 3): string[] {
  const shuffled = [...AVAILABLE_TRAITS].sort(() => Math.random() - 0.5);
  return shuffled.slice(0, Math.min(count, AVAILABLE_TRAITS.length));
}

function generateRandomInterests(count: number = 3): string[] {
  const shuffled = [...AVAILABLE_INTERESTS].sort(() => Math.random() - 0.5);
  return shuffled.slice(0, Math.min(count, AVAILABLE_INTERESTS.length));
}

function generateCharacter(options: { sex: string; firstname?: string; lastname?: string }): MockCharacter {
  const name = options.firstname && options.lastname
    ? { firstname: options.firstname, lastname: options.lastname }
    : generateRandomName(options.sex);

  return {
    id: `char-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    firstname: name.firstname,
    lastname: name.lastname,
    sex: options.sex === 'male' ? 'Male' : 'Female',
    ageYears: 0,
    ageDays: 0,
    status: 'alive',
    health: 50 + Math.floor(Math.random() * 50), // 50-100
    happiness: 50 + Math.floor(Math.random() * 50), // 50-100
    intelligence: Math.floor(Math.random() * 100), // 0-100
    money: 0,
    diamonds: 0,
    traits: generateRandomTraits(3),
    interests: generateRandomInterests(3),
  };
}

function calculateAge(birthday: string): { years: number; days: number } {
  const birthDate = new Date(birthday);
  const now = new Date();
  const diffTime = now.getTime() - birthDate.getTime();
  const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
  const years = Math.floor(diffDays / 365);
  const days = diffDays % 365;
  return { years, days };
}

function ageCharacter(character: MockCharacter, days: number): void {
  character.ageDays += days;
  while (character.ageDays >= 365) {
    character.ageDays -= 365;
    character.ageYears += 1;
  }
}

function getAvailableInterests(): string[] {
  return [...AVAILABLE_INTERESTS];
}

function getAvailableTraits(): string[] {
  return [...AVAILABLE_TRAITS];
}

describe('Character Service', () => {
  describe('generateCharacter', () => {
    it('should generate a new character', () => {
      const character = generateCharacter({ sex: 'male' });

      expect(character).toBeDefined();
      expect(character.id).toBeDefined();
      expect(character.firstname).toBeDefined();
      expect(character.sex).toBe('Male');
    });

    it('should generate female character', () => {
      const character = generateCharacter({ sex: 'female' });

      expect(character.sex).toBe('Female');
    });

    it('should set initial age to 0', () => {
      const character = generateCharacter({ sex: 'male' });

      expect(character.ageYears).toBe(0);
      expect(character.ageDays).toBe(0);
    });

    it('should initialize stats', () => {
      const character = generateCharacter({ sex: 'female' });

      expect(character.health).toBeGreaterThan(0);
      expect(character.happiness).toBeGreaterThan(0);
      expect(character.intelligence).toBeGreaterThanOrEqual(0);
    });

    it('should generate with custom name', () => {
      const character = generateCharacter({
        sex: 'male',
        firstname: 'John',
        lastname: 'Smith',
      });

      expect(character.firstname).toBe('John');
      expect(character.lastname).toBe('Smith');
    });

    it('should set status to alive', () => {
      const character = generateCharacter({ sex: 'male' });

      expect(character.status).toBe('alive');
    });
  });

  describe('generateRandomName', () => {
    it('should generate male name', () => {
      const name = generateRandomName('male');

      expect(typeof name.firstname).toBe('string');
      expect(name.firstname.length).toBeGreaterThan(0);
    });

    it('should generate female name', () => {
      const name = generateRandomName('female');

      expect(typeof name.firstname).toBe('string');
      expect(name.firstname.length).toBeGreaterThan(0);
    });

    it('should generate unique names', () => {
      const names = new Set<string>();
      for (let i = 0; i < 20; i++) {
        const name = generateRandomName('male');
        names.add(name.firstname);
      }

      // Should have some variety
      expect(names.size).toBeGreaterThan(1);
    });

    it('should include lastname', () => {
      const name = generateRandomName('female');

      expect(name.lastname).toBeDefined();
      expect(name.lastname.length).toBeGreaterThan(0);
    });
  });

  describe('generateRandomTraits', () => {
    it('should generate traits array', () => {
      const traits = generateRandomTraits();

      expect(Array.isArray(traits)).toBe(true);
      expect(traits.length).toBeGreaterThan(0);
    });

    it('should generate specific number of traits', () => {
      const traits = generateRandomTraits(5);

      expect(traits.length).toBeLessThanOrEqual(5);
    });

    it('should generate unique traits', () => {
      const traits = generateRandomTraits(10);
      const uniqueTraits = new Set(traits);

      expect(uniqueTraits.size).toBe(traits.length);
    });

    it('should generate valid trait strings', () => {
      const traits = generateRandomTraits();

      traits.forEach(trait => {
        expect(typeof trait).toBe('string');
        expect(trait.length).toBeGreaterThan(0);
      });
    });
  });

  describe('generateRandomInterests', () => {
    it('should generate interests array', () => {
      const interests = generateRandomInterests();

      expect(Array.isArray(interests)).toBe(true);
      expect(interests.length).toBeGreaterThan(0);
    });

    it('should generate specific number of interests', () => {
      const interests = generateRandomInterests(5);

      expect(interests.length).toBeLessThanOrEqual(5);
    });

    it('should generate unique interests', () => {
      const interests = generateRandomInterests(10);
      const uniqueInterests = new Set(interests);

      expect(uniqueInterests.size).toBe(interests.length);
    });

    it('should generate valid interest strings', () => {
      const interests = generateRandomInterests();

      interests.forEach(interest => {
        expect(typeof interest).toBe('string');
        expect(interest.length).toBeGreaterThan(0);
      });
    });
  });

  describe('calculateAge', () => {
    it('should calculate age from birthday', () => {
      const oneYearAgo = new Date();
      oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);

      const age = calculateAge(oneYearAgo.toISOString());

      expect(age.years).toBe(1);
    });

    it('should calculate days correctly', () => {
      const sixMonthsAgo = new Date();
      sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);

      const age = calculateAge(sixMonthsAgo.toISOString());

      expect(age.days).toBeGreaterThan(0);
    });

    it('should handle newborn', () => {
      const today = new Date();

      const age = calculateAge(today.toISOString());

      expect(age.years).toBe(0);
      expect(age.days).toBeLessThanOrEqual(1);
    });

    it('should handle elderly', () => {
      const eightyYearsAgo = new Date();
      eightyYearsAgo.setFullYear(eightyYearsAgo.getFullYear() - 80);

      const age = calculateAge(eightyYearsAgo.toISOString());

      expect(age.years).toBe(80);
    });
  });

  describe('ageCharacter', () => {
    it('should increment age by one day', () => {
      const character = generateCharacter({ sex: 'male' });
      character.ageYears = 10;
      character.ageDays = 100;

      ageCharacter(character, 1);

      expect(character.ageDays).toBe(101);
    });

    it('should increment year when days exceed 365', () => {
      const character = generateCharacter({ sex: 'male' });
      character.ageYears = 10;
      character.ageDays = 364;

      ageCharacter(character, 2);

      expect(character.ageYears).toBe(11);
      expect(character.ageDays).toBe(1);
    });

    it('should age multiple days', () => {
      const character = generateCharacter({ sex: 'male' });
      character.ageYears = 0;
      character.ageDays = 0;

      ageCharacter(character, 30);

      expect(character.ageDays).toBe(30);
    });

    it('should age multiple years', () => {
      const character = generateCharacter({ sex: 'male' });
      character.ageYears = 0;
      character.ageDays = 0;

      ageCharacter(character, 365 * 5);

      expect(character.ageYears).toBe(5);
    });
  });

  describe('getAvailableInterests', () => {
    it('should return list of interests', () => {
      const interests = getAvailableInterests();

      expect(Array.isArray(interests)).toBe(true);
      expect(interests.length).toBeGreaterThan(0);
    });

    it('should include common interests', () => {
      const interests = getAvailableInterests();
      const commonInterests = ['music', 'sports', 'reading', 'movies', 'cooking'];

      const found = commonInterests.filter(ci =>
        interests.some(i => i.toLowerCase().includes(ci))
      );

      expect(found.length).toBeGreaterThan(0);
    });
  });

  describe('getAvailableTraits', () => {
    it('should return list of traits', () => {
      const traits = getAvailableTraits();

      expect(Array.isArray(traits)).toBe(true);
      expect(traits.length).toBeGreaterThan(0);
    });

    it('should include personality traits', () => {
      const traits = getAvailableTraits();
      const personalityTraits = ['outgoing', 'creative', 'ambitious', 'kind', 'shy'];

      const found = personalityTraits.filter(pt =>
        traits.some(t => t.toLowerCase().includes(pt))
      );

      expect(found.length).toBeGreaterThan(0);
    });
  });

  describe('Character lifecycle', () => {
    it('should handle full character lifecycle', () => {
      // Create character
      const character = generateCharacter({ sex: 'female' });
      expect(character.ageYears).toBe(0);

      // Age to child
      ageCharacter(character, 365 * 5);
      expect(character.ageYears).toBe(5);

      // Age to teenager
      ageCharacter(character, 365 * 10);
      expect(character.ageYears).toBe(15);

      // Age to adult
      ageCharacter(character, 365 * 10);
      expect(character.ageYears).toBe(25);

      // Age to elderly
      ageCharacter(character, 365 * 40);
      expect(character.ageYears).toBe(65);
    });
  });

  describe('Character stats initialization', () => {
    it('should initialize health between 0 and 100', () => {
      for (let i = 0; i < 10; i++) {
        const character = generateCharacter({ sex: 'male' });
        expect(character.health).toBeGreaterThanOrEqual(0);
        expect(character.health).toBeLessThanOrEqual(100);
      }
    });

    it('should initialize happiness between 0 and 100', () => {
      for (let i = 0; i < 10; i++) {
        const character = generateCharacter({ sex: 'female' });
        expect(character.happiness).toBeGreaterThanOrEqual(0);
        expect(character.happiness).toBeLessThanOrEqual(100);
      }
    });

    it('should initialize intelligence between 0 and 100', () => {
      for (let i = 0; i < 10; i++) {
        const character = generateCharacter({ sex: 'male' });
        expect(character.intelligence).toBeGreaterThanOrEqual(0);
        expect(character.intelligence).toBeLessThanOrEqual(100);
      }
    });

    it('should initialize money at 0', () => {
      const character = generateCharacter({ sex: 'female' });
      expect(character.money).toBe(0);
    });

    it('should initialize diamonds at 0', () => {
      const character = generateCharacter({ sex: 'male' });
      expect(character.diamonds).toBe(0);
    });
  });

  describe('Name generation variety', () => {
    it('should generate diverse male names', () => {
      const names = new Set<string>();
      for (let i = 0; i < 50; i++) {
        const { firstname } = generateRandomName('male');
        names.add(firstname);
      }

      expect(names.size).toBeGreaterThan(10);
    });

    it('should generate diverse female names', () => {
      const names = new Set<string>();
      for (let i = 0; i < 50; i++) {
        const { firstname } = generateRandomName('female');
        names.add(firstname);
      }

      expect(names.size).toBeGreaterThan(10);
    });

    it('should generate diverse lastnames', () => {
      const names = new Set<string>();
      for (let i = 0; i < 50; i++) {
        const { lastname } = generateRandomName('male');
        if (lastname) names.add(lastname);
      }

      expect(names.size).toBeGreaterThan(10);
    });
  });
});
