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

// Mock person type
interface MockPerson {
  id: string;
  firstname: string;
  sex?: 'male' | 'female';
  ageYears?: number;
  interests?: string[];
  traits?: string[];
  compatibilityScore?: number;
}

// Mock date activities
const DATE_ACTIVITIES = [
  { id: 'date-1', name: 'Coffee Date', energyCost: 10, moneyCost: 20, affinityBonus: 5 },
  { id: 'date-2', name: 'Movie Night', energyCost: 15, moneyCost: 50, affinityBonus: 8 },
  { id: 'date-3', name: 'Dinner Date', energyCost: 20, moneyCost: 100, affinityBonus: 15 },
  { id: 'date-4', name: 'Beach Walk', energyCost: 25, affinityBonus: 12 },
  { id: 'date-5', name: 'Picnic in Park', energyCost: 15, moneyCost: 30, affinityBonus: 10 },
];

// Mock relationship events
const RELATIONSHIP_EVENTS = [
  { id: 'rel-1', title: 'First Date', type: 'milestone', choices: ['Be yourself', 'Try to impress'] },
  { id: 'rel-2', title: 'Anniversary', type: 'milestone', choices: ['Plan something special', 'Keep it simple'] },
  { id: 'rel-3', title: 'Meet the parents', type: 'milestone', choices: ['Be nervous', 'Stay confident'] },
  { id: 'rel-4', title: 'Birthday Surprise', type: 'event', choices: ['Throw a party', 'Intimate dinner'] },
];

// Mock names for match generation
const MALE_NAMES = ['Alex', 'James', 'Michael', 'David', 'Chris', 'Ryan', 'Matt', 'Jake'];
const FEMALE_NAMES = ['Emma', 'Sarah', 'Jessica', 'Ashley', 'Lauren', 'Megan', 'Nicole', 'Rachel'];
const INTERESTS = ['music', 'movies', 'reading', 'cooking', 'hiking', 'sports', 'art', 'gaming', 'travel', 'photography'];
const TRAITS = ['outgoing', 'creative', 'ambitious', 'kind', 'funny', 'adventurous', 'intellectual', 'caring'];

// Mock service functions
function calculateCompatibility(person1: MockPerson, person2: MockPerson): number {
  const interests1 = person1.interests ?? [];
  const interests2 = person2.interests ?? [];
  const traits1 = person1.traits ?? [];
  const traits2 = person2.traits ?? [];

  if (interests1.length === 0 && interests2.length === 0 && traits1.length === 0 && traits2.length === 0) {
    return 50; // Default score
  }

  // Calculate interest overlap
  const sharedInterests = interests1.filter(i => interests2.includes(i)).length;
  const totalInterests = Math.max(interests1.length, interests2.length) || 1;
  const interestScore = (sharedInterests / totalInterests) * 60;

  // Calculate trait compatibility
  const sharedTraits = traits1.filter(t => traits2.includes(t)).length;
  const totalTraits = Math.max(traits1.length, traits2.length) || 1;
  const traitScore = (sharedTraits / totalTraits) * 40;

  return Math.round(Math.min(100, Math.max(0, interestScore + traitScore + 20)));
}

function generateMatch(player: MockPerson): MockPerson | null {
  const oppositeSex = player.sex === 'male' ? 'female' : 'male';
  const names = oppositeSex === 'male' ? MALE_NAMES : FEMALE_NAMES;

  const playerAge = player.ageYears ?? 25;
  const ageOffset = Math.floor(Math.random() * 11) - 5; // -5 to +5

  const randomInterests = [...INTERESTS].sort(() => Math.random() - 0.5).slice(0, 3);
  const randomTraits = [...TRAITS].sort(() => Math.random() - 0.5).slice(0, 2);

  return {
    id: `match-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    firstname: names[Math.floor(Math.random() * names.length)],
    sex: oppositeSex,
    ageYears: Math.max(18, playerAge + ageOffset),
    interests: randomInterests,
    traits: randomTraits,
  };
}

function getAvailableMatches(player: MockPerson, count: number = 5): MockPerson[] {
  const matches: MockPerson[] = [];

  for (let i = 0; i < count; i++) {
    const match = generateMatch(player);
    if (match) {
      match.compatibilityScore = calculateCompatibility(player, match);
      matches.push(match);
    }
  }

  return matches;
}

function getDateActivities(affinityLevel?: number): typeof DATE_ACTIVITIES {
  if (affinityLevel === undefined) return DATE_ACTIVITIES;

  // Higher affinity unlocks more activities
  if (affinityLevel < 30) return DATE_ACTIVITIES.slice(0, 2);
  if (affinityLevel < 60) return DATE_ACTIVITIES.slice(0, 4);
  return DATE_ACTIVITIES;
}

describe('Dating Service', () => {
  let player: MockPerson;

  beforeEach(() => {
    player = {
      id: 'player',
      firstname: 'Player',
      sex: 'male',
      ageYears: 25,
      interests: ['music', 'movies', 'cooking'],
      traits: ['outgoing', 'creative'],
    };
  });

  describe('calculateCompatibility', () => {
    it('should calculate compatibility between two people', () => {
      const match: MockPerson = {
        id: 'match',
        firstname: 'Match',
        interests: ['music', 'sports', 'cooking'],
        traits: ['outgoing', 'ambitious'],
      };

      const score = calculateCompatibility(player, match);

      expect(typeof score).toBe('number');
      expect(score).toBeGreaterThanOrEqual(0);
      expect(score).toBeLessThanOrEqual(100);
    });

    it('should give higher score for more common interests', () => {
      const perfectMatch: MockPerson = {
        id: 'perfect',
        firstname: 'Perfect',
        interests: ['music', 'movies', 'cooking', 'hiking'],
        traits: [],
      };

      const poorMatch: MockPerson = {
        id: 'poor',
        firstname: 'Poor',
        interests: ['sports', 'gaming', 'cars', 'fishing'],
        traits: [],
      };

      const perfectScore = calculateCompatibility(player, perfectMatch);
      const poorScore = calculateCompatibility(player, poorMatch);

      expect(perfectScore).toBeGreaterThan(poorScore);
    });

    it('should handle empty interests', () => {
      const emptyPlayer: MockPerson = { id: 'player', firstname: 'Player', interests: [], traits: [] };
      const emptyMatch: MockPerson = { id: 'match', firstname: 'Match', interests: [], traits: [] };

      const score = calculateCompatibility(emptyPlayer, emptyMatch);
      expect(typeof score).toBe('number');
    });

    it('should handle matching traits', () => {
      const goodMatch: MockPerson = {
        id: 'good',
        firstname: 'Good',
        interests: [],
        traits: ['outgoing', 'creative'],
      };

      const score = calculateCompatibility(player, goodMatch);
      expect(score).toBeGreaterThan(0);
    });
  });

  describe('generateMatch', () => {
    it('should generate a dating match', () => {
      const match = generateMatch(player);

      expect(match).toBeDefined();
      if (match) {
        expect(match.id).toBeDefined();
        expect(match.firstname).toBeDefined();
        expect(match.ageYears).toBeDefined();
      }
    });

    it('should generate age-appropriate matches', () => {
      const youngPlayer: MockPerson = { id: 'young', firstname: 'Young', sex: 'female', ageYears: 18 };
      const olderPlayer: MockPerson = { id: 'older', firstname: 'Older', sex: 'male', ageYears: 40 };

      const youngMatch = generateMatch(youngPlayer);
      const olderMatch = generateMatch(olderPlayer);

      if (youngMatch) {
        expect(Math.abs(youngMatch.ageYears! - youngPlayer.ageYears!)).toBeLessThanOrEqual(10);
      }

      if (olderMatch) {
        expect(Math.abs(olderMatch.ageYears! - olderPlayer.ageYears!)).toBeLessThanOrEqual(15);
      }
    });

    it('should assign interests to matches', () => {
      const match = generateMatch(player);

      if (match) {
        expect(match.interests).toBeDefined();
        expect(Array.isArray(match.interests)).toBe(true);
      }
    });

    it('should assign traits to matches', () => {
      const match = generateMatch(player);

      if (match) {
        expect(match.traits).toBeDefined();
        expect(Array.isArray(match.traits)).toBe(true);
      }
    });
  });

  describe('getAvailableMatches', () => {
    it('should return multiple matches', () => {
      const matches = getAvailableMatches(player, 5);

      expect(Array.isArray(matches)).toBe(true);
      expect(matches.length).toBeLessThanOrEqual(5);
    });

    it('should return matches with compatibility scores', () => {
      const matches = getAvailableMatches(player, 3);

      matches.forEach(match => {
        expect(match.compatibilityScore).toBeDefined();
        expect(match.compatibilityScore).toBeGreaterThanOrEqual(0);
        expect(match.compatibilityScore).toBeLessThanOrEqual(100);
      });
    });
  });

  describe('Date Activities', () => {
    it('should have date activities defined', () => {
      expect(DATE_ACTIVITIES).toBeDefined();
      expect(Array.isArray(DATE_ACTIVITIES)).toBe(true);
    });

    it('should have activities with required properties', () => {
      DATE_ACTIVITIES.forEach(activity => {
        expect(activity).toHaveProperty('id');
        expect(activity).toHaveProperty('name');
        expect(typeof activity.id).toBe('string');
        expect(typeof activity.name).toBe('string');
      });
    });

    it('should have activities with costs', () => {
      DATE_ACTIVITIES.forEach(activity => {
        if (activity.moneyCost !== undefined) {
          expect(activity.moneyCost).toBeGreaterThanOrEqual(0);
        }
        if (activity.energyCost !== undefined) {
          expect(activity.energyCost).toBeGreaterThanOrEqual(0);
        }
      });
    });

    it('should have activities with affinity effects', () => {
      const activitiesWithAffinity = DATE_ACTIVITIES.filter(a => a.affinityBonus !== undefined);
      expect(activitiesWithAffinity.length).toBeGreaterThan(0);
    });

    it('should include common date types', () => {
      const names = DATE_ACTIVITIES.map(a => a.name.toLowerCase());
      const commonDates = ['dinner', 'movie', 'walk', 'coffee', 'picnic'];
      const foundDates = commonDates.filter(d => names.some(n => n.includes(d)));

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

  describe('getDateActivities', () => {
    it('should return available date activities', () => {
      const activities = getDateActivities();

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

    it('should filter by relationship level if provided', () => {
      const newRelationship = getDateActivities(10);
      const establishedRelationship = getDateActivities(80);

      expect(newRelationship.length).toBeGreaterThanOrEqual(0);
      expect(establishedRelationship.length).toBeGreaterThanOrEqual(0);
    });
  });

  describe('Relationship Events', () => {
    it('should have relationship events defined', () => {
      expect(RELATIONSHIP_EVENTS).toBeDefined();
      expect(Array.isArray(RELATIONSHIP_EVENTS)).toBe(true);
    });

    it('should have events with required properties', () => {
      RELATIONSHIP_EVENTS.forEach(event => {
        expect(event).toHaveProperty('id');
        expect(event).toHaveProperty('title');
        expect(typeof event.id).toBe('string');
        expect(typeof event.title).toBe('string');
      });
    });

    it('should have events with choices', () => {
      RELATIONSHIP_EVENTS.forEach(event => {
        if (event.choices) {
          expect(Array.isArray(event.choices)).toBe(true);
          expect(event.choices.length).toBeGreaterThan(0);
        }
      });
    });

    it('should include milestone events', () => {
      const milestones = RELATIONSHIP_EVENTS.filter(e =>
        e.type === 'milestone' ||
        e.title.toLowerCase().includes('anniversary') ||
        e.title.toLowerCase().includes('birthday') ||
        e.title.toLowerCase().includes('first')
      );

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

  describe('Compatibility scoring edge cases', () => {
    it('should handle null interests', () => {
      const noInterests1: MockPerson = { id: 'p1', firstname: 'P1' };
      const noInterests2: MockPerson = { id: 'p2', firstname: 'P2' };

      expect(() => calculateCompatibility(noInterests1, noInterests2)).not.toThrow();
    });

    it('should handle very different ages', () => {
      const young: MockPerson = { id: 'young', firstname: 'Young', ageYears: 20 };
      const old: MockPerson = { id: 'old', firstname: 'Old', ageYears: 60 };

      const score = calculateCompatibility(young, old);
      expect(typeof score).toBe('number');
    });

    it('should handle same person comparison', () => {
      const person: MockPerson = {
        id: 'same',
        firstname: 'Same',
        interests: ['music'],
        traits: ['outgoing'],
      };

      const score = calculateCompatibility(person, person);
      expect(score).toBeGreaterThan(50);
    });
  });

  describe('Match generation variety', () => {
    it('should generate diverse matches', () => {
      const matches: MockPerson[] = [];
      for (let i = 0; i < 10; i++) {
        const match = generateMatch(player);
        if (match) matches.push(match);
      }

      const names = new Set(matches.map(m => m.firstname));
      expect(names.size).toBeGreaterThan(1);
    });

    it('should generate matches with variety of interests', () => {
      const matches: MockPerson[] = [];
      for (let i = 0; i < 10; i++) {
        const match = generateMatch(player);
        if (match) matches.push(match);
      }

      const allInterests = new Set<string>();
      matches.forEach(m => {
        m.interests?.forEach(i => allInterests.add(i));
      });

      expect(allInterests.size).toBeGreaterThan(0);
    });
  });
});
