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

// Mock relationship types
type RelationshipType = 'friend' | 'best_friend' | 'family' | 'romantic' | 'crush' | 'enemy' | 'acquaintance';

// Mock person type for relationships
interface MockPerson {
  id: string;
  firstname: string;
  lastname?: string;
  ageYears?: number;
  relationships?: string[];
  affinity?: number;
  interests?: string[];
  traits?: string[];
  compatibility?: number;
}

// Mock player type
interface MockPlayer {
  id: string;
  firstname: string;
  ageYears: number;
  interests?: string[];
  r: MockPerson[];
}

// Mock relationship milestone
interface RelationshipMilestone {
  type: string;
  message: string;
  affinityRequired: number;
}

// Mock service functions
function getRelationshipStatus(person: MockPerson): { type: RelationshipType; level: string } {
  const affinity = person.affinity ?? 50;

  if (person.relationships?.includes('romantic') || person.relationships?.includes('spouse')) {
    if (affinity >= 90) return { type: 'romantic', level: 'Soulmates' };
    if (affinity >= 70) return { type: 'romantic', level: 'In Love' };
    return { type: 'romantic', level: 'Dating' };
  }

  if (person.relationships?.includes('enemy')) {
    return { type: 'enemy', level: 'Enemy' };
  }

  if (person.relationships?.some(r => ['mother', 'father', 'sibling', 'child'].includes(r))) {
    if (affinity >= 80) return { type: 'family', level: 'Close Family' };
    return { type: 'family', level: 'Family' };
  }

  if (affinity >= 80) return { type: 'best_friend', level: 'Best Friend' };
  if (affinity >= 50) return { type: 'friend', level: 'Friend' };
  if (affinity >= 25) return { type: 'acquaintance', level: 'Acquaintance' };

  return { type: 'acquaintance', level: 'Stranger' };
}

function calculateCompatibility(person1: MockPerson, person2: MockPerson): number {
  const interests1 = person1.interests ?? [];
  const interests2 = person2.interests ?? [];

  if (interests1.length === 0 || interests2.length === 0) return 50;

  const sharedInterests = interests1.filter(i => interests2.includes(i));
  const compatibilityScore = (sharedInterests.length / Math.max(interests1.length, interests2.length)) * 100;

  return Math.min(100, Math.max(0, Math.round(compatibilityScore)));
}

function improveRelationship(person: MockPerson, amount: number): number {
  const currentAffinity = person.affinity ?? 50;
  const newAffinity = Math.min(100, Math.max(0, currentAffinity + amount));
  person.affinity = newAffinity;
  return newAffinity;
}

function damageRelationship(person: MockPerson, amount: number): number {
  const currentAffinity = person.affinity ?? 50;
  const newAffinity = Math.min(100, Math.max(0, currentAffinity - amount));
  person.affinity = newAffinity;
  return newAffinity;
}

function generateFamilyMember(player: MockPlayer, relationType: string): MockPerson {
  const member: MockPerson = {
    id: `family-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    firstname: ['John', 'Jane', 'Michael', 'Sarah'][Math.floor(Math.random() * 4)],
    lastname: 'Smith',
    ageYears: relationType === 'child' ? Math.max(0, player.ageYears - 20) : player.ageYears + 25,
    relationships: [relationType],
    affinity: 70 + Math.floor(Math.random() * 30),
  };

  return member;
}

function generateFriend(player: MockPlayer): MockPerson {
  const friend: MockPerson = {
    id: `friend-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    firstname: ['Alex', 'Sam', 'Jordan', 'Taylor'][Math.floor(Math.random() * 4)],
    ageYears: player.ageYears + Math.floor(Math.random() * 11) - 5, // ±5 years
    relationships: ['friend'],
    affinity: 50 + Math.floor(Math.random() * 30),
    interests: player.interests?.slice(0, 2) ?? [],
  };

  return friend;
}

function checkRelationshipMilestone(person: MockPerson): RelationshipMilestone | null {
  const affinity = person.affinity ?? 0;

  if (person.relationships?.includes('friend') && affinity >= 80) {
    return { type: 'best_friend', message: 'You became best friends!', affinityRequired: 80 };
  }

  if (person.relationships?.includes('romantic') && affinity >= 90) {
    return { type: 'engaged', message: 'You got engaged!', affinityRequired: 90 };
  }

  return null;
}

describe('Relationships Service', () => {
  let player: MockPlayer;
  let person: MockPerson;

  beforeEach(() => {
    player = {
      id: 'player-1',
      firstname: 'Test',
      ageYears: 25,
      interests: ['Music', 'Sports', 'Reading'],
      r: [],
    };

    person = {
      id: 'person-1',
      firstname: 'Friend',
      ageYears: 26,
      affinity: 50,
      relationships: ['friend'],
      interests: ['Music', 'Movies'],
    };
  });

  describe('getRelationshipStatus', () => {
    it('should return relationship type and level', () => {
      const status = getRelationshipStatus(person);

      expect(status).toBeDefined();
      expect(status.type).toBeDefined();
      expect(status.level).toBeDefined();
    });

    it('should identify friends', () => {
      person.affinity = 60;
      person.relationships = ['friend'];
      const status = getRelationshipStatus(person);

      expect(status.type).toBe('friend');
    });

    it('should identify best friends', () => {
      person.affinity = 85;
      person.relationships = ['friend'];
      const status = getRelationshipStatus(person);

      expect(status.type).toBe('best_friend');
    });

    it('should identify romantic relationships', () => {
      person.affinity = 75;
      person.relationships = ['romantic'];
      const status = getRelationshipStatus(person);

      expect(status.type).toBe('romantic');
    });

    it('should identify family members', () => {
      person.affinity = 80;
      person.relationships = ['mother'];
      const status = getRelationshipStatus(person);

      expect(status.type).toBe('family');
    });

    it('should identify enemies', () => {
      person.affinity = 10;
      person.relationships = ['enemy'];
      const status = getRelationshipStatus(person);

      expect(status.type).toBe('enemy');
    });
  });

  describe('calculateCompatibility', () => {
    it('should calculate compatibility between two people', () => {
      const person2: MockPerson = {
        id: 'p2',
        firstname: 'Test2',
        interests: ['Music', 'Sports'],
      };

      const compatibility = calculateCompatibility(person, person2);

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

    it('should return higher compatibility for shared interests', () => {
      const sameInterests: MockPerson = { id: '1', firstname: 'A', interests: ['Music', 'Movies'] };
      const differentInterests: MockPerson = { id: '2', firstname: 'B', interests: ['Sports', 'Cooking'] };

      const sameCompat = calculateCompatibility(person, sameInterests);
      const diffCompat = calculateCompatibility(person, differentInterests);

      expect(sameCompat).toBeGreaterThan(diffCompat);
    });

    it('should handle people without interests', () => {
      const noInterests: MockPerson = { id: '1', firstname: 'A' };
      const compatibility = calculateCompatibility(person, noInterests);

      expect(compatibility).toBe(50); // Default
    });
  });

  describe('improveRelationship', () => {
    it('should increase affinity', () => {
      person.affinity = 50;
      const newAffinity = improveRelationship(person, 10);

      expect(newAffinity).toBe(60);
    });

    it('should not exceed 100', () => {
      person.affinity = 95;
      const newAffinity = improveRelationship(person, 20);

      expect(newAffinity).toBeLessThanOrEqual(100);
    });
  });

  describe('damageRelationship', () => {
    it('should decrease affinity', () => {
      person.affinity = 50;
      const newAffinity = damageRelationship(person, 10);

      expect(newAffinity).toBe(40);
    });

    it('should not go below 0', () => {
      person.affinity = 5;
      const newAffinity = damageRelationship(person, 20);

      expect(newAffinity).toBe(0);
    });
  });

  describe('generateFamilyMember', () => {
    it('should generate a mother', () => {
      const mother = generateFamilyMember(player, 'mother');

      expect(mother).toBeDefined();
      expect(mother.id).toBeDefined();
      expect(mother.firstname).toBeDefined();
      expect(mother.relationships).toContain('mother');
    });

    it('should generate a father', () => {
      const father = generateFamilyMember(player, 'father');

      expect(father).toBeDefined();
      expect(father.relationships).toContain('father');
    });

    it('should generate a sibling', () => {
      const sibling = generateFamilyMember(player, 'sibling');

      expect(sibling).toBeDefined();
      expect(sibling.lastname).toBeDefined();
    });

    it('should set family affinity', () => {
      const mother = generateFamilyMember(player, 'mother');

      expect(mother.affinity).toBeGreaterThanOrEqual(70);
    });
  });

  describe('generateFriend', () => {
    it('should generate a friend', () => {
      const friend = generateFriend(player);

      expect(friend).toBeDefined();
      expect(friend.id).toBeDefined();
      expect(friend.firstname).toBeDefined();
    });

    it('should generate age-appropriate friends', () => {
      const friend = generateFriend(player);
      const ageDiff = Math.abs((friend.ageYears ?? 0) - player.ageYears);

      expect(ageDiff).toBeLessThanOrEqual(5);
    });

    it('should give friends interests', () => {
      const friend = generateFriend(player);

      expect(friend.interests).toBeDefined();
    });

    it('should share some interests with player', () => {
      player.interests = ['Music', 'Sports', 'Reading'];
      const friend = generateFriend(player);
      const sharedInterests = (friend.interests ?? []).filter(i => player.interests?.includes(i));

      expect(sharedInterests.length).toBeGreaterThanOrEqual(0);
    });
  });

  describe('Affinity ranges', () => {
    const affinityLevels = [0, 25, 50, 75, 100];

    affinityLevels.forEach(level => {
      it(`should handle affinity level ${level}`, () => {
        person.affinity = level;
        const status = getRelationshipStatus(person);
        expect(status).toBeDefined();
      });
    });
  });

  describe('Relationship transitions', () => {
    it('should handle friend to best friend', () => {
      person.affinity = 85;
      person.relationships = ['friend'];

      const milestone = checkRelationshipMilestone(person);

      expect(milestone).toBeDefined();
      expect(milestone?.type).toBe('best_friend');
    });

    it('should handle romantic progression', () => {
      const partner: MockPerson = {
        id: 'partner-1',
        firstname: 'Partner',
        relationships: ['romantic'],
        affinity: 95,
      };

      const milestone = checkRelationshipMilestone(partner);

      expect(milestone?.type).toBe('engaged');
    });
  });

  describe('Relationship types', () => {
    const relationshipTypes = ['friend', 'romantic', 'family', 'acquaintance'];

    relationshipTypes.forEach(type => {
      it(`should handle ${type} relationship`, () => {
        if (type === 'family') {
          person.relationships = ['mother'];
        } else if (type === 'romantic') {
          person.relationships = ['romantic'];
        } else {
          person.relationships = [type];
        }

        const status = getRelationshipStatus(person);
        expect(status).toBeDefined();
      });
    });
  });

  describe('Multiple relationships', () => {
    it('should track multiple relationships', () => {
      player.r = [
        { id: 'f1', firstname: 'Friend1', relationships: ['friend'], affinity: 60 },
        { id: 'f2', firstname: 'Friend2', relationships: ['friend'], affinity: 70 },
        { id: 'm1', firstname: 'Mom', relationships: ['mother'], affinity: 80 },
      ];

      expect(player.r).toHaveLength(3);
    });

    it('should maintain individual affinities', () => {
      player.r = [
        { id: 'f1', firstname: 'Friend1', affinity: 60 },
        { id: 'f2', firstname: 'Friend2', affinity: 80 },
      ];

      improveRelationship(player.r[0], 10);

      expect(player.r[0].affinity).toBe(70);
      expect(player.r[1].affinity).toBe(80);
    });
  });

  describe('Relationship effects', () => {
    it('should affect happiness with high affinity friends', () => {
      person.affinity = 90;
      const status = getRelationshipStatus(person);

      expect(status.level).toBe('Best Friend');
    });

    it('should reflect strained relationships', () => {
      person.affinity = 10;
      person.relationships = [];
      const status = getRelationshipStatus(person);

      expect(status.level).toBe('Stranger');
    });
  });
});
