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

// Mock job data
interface Job {
  id: string;
  title: string;
  salary: number;
  minAge?: number;
  educationRequired?: string;
  hoursPerWeek?: number;
  energyCost?: number;
  happinessEffect?: number;
  prestigeBonus?: number;
  nextPosition?: string;
}

const JOBS: Job[] = [
  { id: 'job-1', title: 'Babysitter', salary: 10, minAge: 14, hoursPerWeek: 10, energyCost: 10 },
  { id: 'job-2', title: 'Grocery Clerk', salary: 12, minAge: 16, hoursPerWeek: 20, energyCost: 15 },
  { id: 'job-3', title: 'Fast Food Worker', salary: 11, minAge: 16, hoursPerWeek: 25, energyCost: 20 },
  { id: 'job-4', title: 'Retail Associate', salary: 15, minAge: 18, educationRequired: 'High School', hoursPerWeek: 35, energyCost: 20 },
  { id: 'job-5', title: 'Office Assistant', salary: 18, minAge: 18, educationRequired: 'High School', hoursPerWeek: 40, energyCost: 15, nextPosition: 'job-6' },
  { id: 'job-6', title: 'Office Manager', salary: 35, minAge: 21, educationRequired: 'High School', hoursPerWeek: 45, energyCost: 25, prestigeBonus: 10 },
  { id: 'job-7', title: 'Junior Developer', salary: 50, minAge: 22, educationRequired: 'College', hoursPerWeek: 40, energyCost: 30, happinessEffect: 5, nextPosition: 'job-8' },
  { id: 'job-8', title: 'Senior Developer', salary: 80, minAge: 25, educationRequired: 'College', hoursPerWeek: 45, energyCost: 35, prestigeBonus: 20, happinessEffect: 10 },
  { id: 'job-9', title: 'Doctor', salary: 120, minAge: 30, educationRequired: 'Graduate', hoursPerWeek: 50, energyCost: 40, prestigeBonus: 50 },
  { id: 'job-10', title: 'Lawyer', salary: 100, minAge: 28, educationRequired: 'Graduate', hoursPerWeek: 50, energyCost: 35, prestigeBonus: 40 },
];

// Mock person type
interface MockPerson {
  id: string;
  firstname?: string;
  ageYears: number;
  education: string;
  occupation?: string;
  prestige?: number;
  jobStartDate?: string;
}

// Mock service functions
function getAvailableJobs(age?: number, education?: string): Job[] {
  let jobs = [...JOBS];

  if (age !== undefined) {
    jobs = jobs.filter(j => (j.minAge ?? 0) <= age);
  }

  if (education) {
    const educationLevels = ['None', 'High School', 'College', 'Graduate'];
    const playerLevel = educationLevels.indexOf(education);

    jobs = jobs.filter(j => {
      const requiredLevel = educationLevels.indexOf(j.educationRequired ?? 'None');
      return requiredLevel <= playerLevel;
    });
  }

  return jobs;
}

function getJobById(jobId: string): Job | undefined {
  return JOBS.find(j => j.id === jobId);
}

function applyForJob(person: MockPerson, jobId: string): { success: boolean; message?: string } {
  if (person.occupation) {
    return { success: false, message: 'Already employed' };
  }

  const job = getJobById(jobId);
  if (!job) {
    return { success: false, message: 'Job not found' };
  }

  if (job.minAge && person.ageYears < job.minAge) {
    return { success: false, message: 'Too young' };
  }

  const educationLevels = ['None', 'High School', 'College', 'Graduate'];
  if (job.educationRequired) {
    const requiredLevel = educationLevels.indexOf(job.educationRequired);
    const playerLevel = educationLevels.indexOf(person.education);
    if (playerLevel < requiredLevel) {
      return { success: false, message: 'Education requirement not met' };
    }
  }

  person.occupation = job.title;
  return { success: true };
}

function quitJob(person: MockPerson, _jobId: string): { success: boolean; message?: string } {
  if (!person.occupation) {
    return { success: false, message: 'Not employed' };
  }

  person.occupation = undefined;
  return { success: true };
}

function calculateSalary(job: Job, yearsExperience: number, performanceMultiplier: number = 1): number {
  const experienceBonus = 1 + (yearsExperience * 0.05);
  return Math.floor(job.salary * experienceBonus * performanceMultiplier);
}

function checkPromotion(person: MockPerson): { eligible: boolean; reason?: string } {
  if (!person.occupation) {
    return { eligible: false, reason: 'Not employed' };
  }

  if (person.jobStartDate) {
    const startDate = new Date(person.jobStartDate);
    const now = new Date();
    const monthsEmployed = (now.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24 * 30);
    if (monthsEmployed < 6) {
      return { eligible: false, reason: 'Minimum tenure not met' };
    }
  }

  const prestige = person.prestige ?? 0;
  if (prestige < 30) {
    return { eligible: false, reason: 'Prestige too low' };
  }

  return { eligible: true };
}

describe('Jobs Service', () => {
  let person: MockPerson;

  beforeEach(() => {
    person = {
      id: 'player-1',
      firstname: 'Test',
      ageYears: 25,
      education: 'High School',
      occupation: undefined,
      prestige: 50,
    };
  });

  describe('getAvailableJobs', () => {
    it('should return list of available jobs', () => {
      const jobs = getAvailableJobs();

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

    it('should filter jobs by age', () => {
      const teenJobs = getAvailableJobs(16);
      const adultJobs = getAvailableJobs(25);

      expect(Array.isArray(teenJobs)).toBe(true);
      expect(Array.isArray(adultJobs)).toBe(true);
      expect(adultJobs.length).toBeGreaterThanOrEqual(teenJobs.length);
    });

    it('should filter jobs by education', () => {
      const noEducationJobs = getAvailableJobs(25, 'None');
      const highSchoolJobs = getAvailableJobs(25, 'High School');
      const collegeJobs = getAvailableJobs(25, 'College');

      expect(Array.isArray(noEducationJobs)).toBe(true);
      expect(Array.isArray(highSchoolJobs)).toBe(true);
      expect(Array.isArray(collegeJobs)).toBe(true);
    });

    it('should have jobs with required properties', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        expect(job).toHaveProperty('id');
        expect(job).toHaveProperty('title');
        expect(typeof job.id).toBe('string');
        expect(typeof job.title).toBe('string');
      });
    });

    it('should have jobs with salary information', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        expect(typeof job.salary).toBe('number');
        expect(job.salary).toBeGreaterThan(0);
      });
    });
  });

  describe('getJobById', () => {
    it('should find job by ID', () => {
      const jobs = getAvailableJobs();
      const found = getJobById(jobs[0].id);

      expect(found).toBeDefined();
      expect(found?.id).toBe(jobs[0].id);
    });

    it('should return undefined for non-existent job', () => {
      const found = getJobById('non-existent-job-12345');
      expect(found).toBeUndefined();
    });
  });

  describe('applyForJob', () => {
    it('should allow applying for a valid job', () => {
      const jobs = getAvailableJobs(25, 'High School');
      const result = applyForJob(person, jobs[0].id);

      expect(result.success).toBe(true);
    });

    it('should reject application for unqualified job', () => {
      person.education = 'None';
      const collegeJobs = JOBS.filter(j => j.educationRequired === 'College');
      const result = applyForJob(person, collegeJobs[0].id);

      expect(result.success).toBe(false);
    });

    it('should not allow applying if already employed', () => {
      person.occupation = 'Current Job';
      const jobs = getAvailableJobs();
      const result = applyForJob(person, jobs[0].id);

      expect(result.success).toBe(false);
    });

    it('should reject if too young', () => {
      person.ageYears = 14;
      const result = applyForJob(person, 'job-7'); // Junior Developer requires 22

      expect(result.success).toBe(false);
    });
  });

  describe('quitJob', () => {
    it('should allow quitting current job', () => {
      person.occupation = 'Current Job';
      const result = quitJob(person, 'job-id');

      expect(result.success).toBe(true);
      expect(person.occupation).toBeUndefined();
    });

    it('should fail if not employed', () => {
      person.occupation = undefined;
      const result = quitJob(person, 'any-job');

      expect(result.success).toBe(false);
    });
  });

  describe('calculateSalary', () => {
    it('should calculate base salary for a job', () => {
      const job = JOBS[0];
      const salary = calculateSalary(job, 1);

      expect(typeof salary).toBe('number');
      expect(salary).toBeGreaterThan(0);
    });

    it('should increase salary with experience', () => {
      const job = JOBS[0];
      const newbySalary = calculateSalary(job, 1);
      const veteranSalary = calculateSalary(job, 10);

      expect(veteranSalary).toBeGreaterThan(newbySalary);
    });

    it('should factor in performance level', () => {
      const job = JOBS[0];
      const lowPerformance = calculateSalary(job, 1, 0.5);
      const highPerformance = calculateSalary(job, 1, 1.5);

      expect(highPerformance).toBeGreaterThan(lowPerformance);
    });
  });

  describe('checkPromotion', () => {
    it('should check if eligible for promotion', () => {
      person.occupation = 'Junior Developer';
      person.prestige = 50;
      person.jobStartDate = new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString(); // 1 year ago

      const result = checkPromotion(person);

      expect(result).toBeDefined();
      expect(typeof result.eligible).toBe('boolean');
    });

    it('should require minimum tenure for promotion', () => {
      person.occupation = 'Junior Developer';
      person.jobStartDate = new Date().toISOString(); // Just started

      const result = checkPromotion(person);

      expect(result.eligible).toBe(false);
    });

    it('should factor in prestige for promotion', () => {
      const lowPrestige: MockPerson = {
        id: '1',
        ageYears: 30,
        education: 'College',
        occupation: 'Developer',
        prestige: 10,
      };

      const highPrestige: MockPerson = {
        id: '2',
        ageYears: 30,
        education: 'College',
        occupation: 'Developer',
        prestige: 90,
      };

      const lowResult = checkPromotion(lowPrestige);
      const highResult = checkPromotion(highPrestige);

      expect(lowResult.eligible).toBe(false);
      expect(highResult.eligible).toBe(true);
    });
  });

  describe('Job categories', () => {
    it('should have entry-level jobs', () => {
      const jobs = getAvailableJobs(18, 'High School');

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

    it('should have professional jobs', () => {
      const jobs = getAvailableJobs(30, 'College');

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

    it('should have part-time jobs for teens', () => {
      const jobs = getAvailableJobs(16);

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

  describe('Job requirements', () => {
    it('should have age requirements', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        if (job.minAge !== undefined) {
          expect(job.minAge).toBeGreaterThanOrEqual(14);
        }
      });
    });

    it('should have education requirements', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        if (job.educationRequired !== undefined) {
          expect(typeof job.educationRequired).toBe('string');
        }
      });
    });

    it('should have schedule information', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        if (job.hoursPerWeek !== undefined) {
          expect(job.hoursPerWeek).toBeGreaterThan(0);
          expect(job.hoursPerWeek).toBeLessThanOrEqual(60);
        }
      });
    });
  });

  describe('Job progression', () => {
    it('should have career paths', () => {
      const jobs = getAvailableJobs();
      const jobsWithProgression = jobs.filter(j => j.nextPosition !== undefined);

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

    it('should have increasing salaries in career path', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        if (job.nextPosition && job.salary) {
          const nextJob = getJobById(job.nextPosition);
          if (nextJob && nextJob.salary) {
            expect(nextJob.salary).toBeGreaterThanOrEqual(job.salary);
          }
        }
      });
    });
  });

  describe('Job stats effects', () => {
    it('should have energy cost', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        if (job.energyCost !== undefined) {
          expect(job.energyCost).toBeGreaterThanOrEqual(0);
        }
      });
    });

    it('should have happiness effects', () => {
      const jobs = getAvailableJobs();

      jobs.forEach(job => {
        if (job.happinessEffect !== undefined) {
          expect(typeof job.happinessEffect).toBe('number');
        }
      });
    });

    it('should have prestige bonuses', () => {
      const jobs = getAvailableJobs();
      const jobsWithPrestige = jobs.filter(j => j.prestigeBonus !== undefined);

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