import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';

@Injectable()
export class JobMatchesService {
  constructor(private readonly dataSource: DataSource) {}

  async matchJobWithSeekers(jobPostId: number): Promise<void> {
    // Get job post details
    const jobPost = await this.dataSource
      .createQueryBuilder()
      .select([
        'jp.id AS job_post_id',
        'jp.role_id',
        'jp.company_id',
        'jp.title',
        'jp.salary_min',
        'jp.salary_max',
      ])
      .from('job_posts', 'jp')
      .where('jp.id = :jobPostId', { jobPostId })
      .getRawOne();

    if (!jobPost) {
      throw new Error('Job post not found');
    }

    // Get job post skills
    const jobSkills = await this.dataSource
      .createQueryBuilder()
      .select('jps.skill_id')
      .from('job_post_skills', 'jps')
      .where('jps.job_post_id = :jobPostId', { jobPostId })
      .getRawMany();

    const jobSkillIds = jobSkills.map((skill) => skill.skill_id);

    // Get all seekers with preferences
    const seekersWithPreferences = await this.dataSource
      .createQueryBuilder()
      .select([
        'sp.user_id AS seeker_id',
        'sp.id AS preference_id',
        'spr.role_id AS preferred_role_id',
        'sps.skill_id AS preferred_skill_id',
      ])
      .from('seeker_preferences', 'sp')
      .leftJoin(
        'seeker_preferred_roles',
        'spr',
        'spr.seeker_preference_id = sp.id',
      )
      .leftJoin(
        'seeker_preferred_skills',
        'sps',
        'sps.seeker_preference_id = sp.id',
      )
      .getRawMany();

    // Group seekers by their preferences
    const seekerPreferences = new Map();
    seekersWithPreferences.forEach((pref) => {
      if (!seekerPreferences.has(pref.seeker_id)) {
        seekerPreferences.set(pref.seeker_id, {
          seeker_id: pref.seeker_id,
          preference_id: pref.preference_id,
          roles: [],
          skills: [],
        });
      }

      if (pref.preferred_role_id) {
        seekerPreferences
          .get(pref.seeker_id)
          .roles.push(pref.preferred_role_id);
      }
      if (pref.preferred_skill_id) {
        seekerPreferences
          .get(pref.seeker_id)
          .skills.push(pref.preferred_skill_id);
      }
    });

    // Match each seeker with the job
    for (const [seekerId, preferences] of seekerPreferences) {
      const matchResult = await this.calculateMatch(
        seekerId,
        preferences,
        jobPost,
        jobSkillIds,
      );

      if (matchResult.isMatch) {
        // Insert into job_matches
        await this.dataSource
          .createQueryBuilder()
          .insert()
          .into('job_matches')
          .values({
            seeker_id: seekerId,
            job_post_id: jobPostId,
            match_score: matchResult.matchScore,
          })
          .execute();

        // Insert into job_alerts
        await this.dataSource
          .createQueryBuilder()
          .insert()
          .into('job_alerts')
          .values({
            seeker_id: seekerId,
            job_post_id: jobPostId,
            match_type: matchResult.matchType,
          })
          .execute();
      }
    }
  }

  private async calculateMatch(
    seekerId: number,
    preferences: {
      roles: number[];
      skills: number[];
    },
    jobPost: any,
    jobSkillIds: number[],
  ): Promise<{
    isMatch: boolean;
    matchScore: number;
    matchType: 'role' | 'skill' | 'both';
  }> {
    let roleMatch = false;
    let skillMatch = false;
    let matchedSkills = 0;
    const totalPreferredSkills = preferences.skills.length;

    // Check role match
    if (preferences.roles.length > 0 && jobPost.role_id) {
      roleMatch = preferences.roles.includes(jobPost.role_id);
    }

    // Check skill match
    if (preferences.skills.length > 0 && jobSkillIds.length > 0) {
      const intersection = preferences.skills.filter((skillId) =>
        jobSkillIds.includes(skillId),
      );
      matchedSkills = intersection.length;
      skillMatch = matchedSkills > 0;
    } else if (preferences.skills.length === 0) {
      // If seeker has no preferred skills, don't consider skill matching
      skillMatch = false;
    }

    // Determine if there's a match
    const isMatch = roleMatch || skillMatch;
    if (!isMatch) {
      return { isMatch: false, matchScore: 0, matchType: 'role' };
    }

    // Calculate match score
    let matchScore = 0;
    let matchType: 'role' | 'skill' | 'both';

    if (roleMatch && skillMatch) {
      matchType = 'both';
      matchScore = (matchedSkills / totalPreferredSkills) * 100 + 20; // Bonus for role match
    } else if (roleMatch) {
      matchType = 'role';
      matchScore = 100; // Full score for role match
    } else {
      matchType = 'skill';
      matchScore = (matchedSkills / totalPreferredSkills) * 100;
    }

    return { isMatch, matchScore: Math.round(matchScore), matchType };
  }

  async getSeekerJobAlerts(
    seekerId: number,
    page: number = 1,
    limit: number = 10,
    matchType?: string,
    title?: string,
  ): Promise<any> {
    const offset = (page - 1) * limit;

    let query = this.dataSource
      .createQueryBuilder()
      .select([
        'ja.id AS alert_id',
        'ja.match_type',
        'jm.match_score',
        'jp.id AS job_post_id',
        'jp.title',
        'jp.salary_min',
        'jp.salary_max',
        'jp.description',
        'company.name AS company_name',
        'city.city_name AS city',
        'state.state_name AS state',
        'country.country_name AS country',
      ])
      .from('job_alerts', 'ja')
      .leftJoin(
        'job_matches',
        'jm',
        'jm.seeker_id = ja.seeker_id AND jm.job_post_id = ja.job_post_id',
      )
      .leftJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
      .leftJoin('companies', 'company', 'company.id = jp.company_id')
      .leftJoin('city', 'city', 'city.city_id = jp.city_id')
      .leftJoin('state', 'state', 'state.state_id = jp.state_id')
      .leftJoin('country', 'country', 'country.country_id = jp.country_id')
      .where('ja.seeker_id = :seekerId', { seekerId });

    // Apply filters
    if (matchType) {
      query = query.andWhere('ja.match_type = :matchType', { matchType });
    }

    if (title) {
      query = query.andWhere('jp.title LIKE :title', { title: `%${title}%` });
    }

    // Get total count for pagination
    const totalCount = await query.getCount();

    // Get paginated results
    const results = await query
      .orderBy('ja.created', 'DESC')
      .offset(offset)
      .limit(limit)
      .getRawMany();

    return {
      data: results,
      pagination: {
        page,
        limit,
        total: totalCount,
        totalPages: Math.ceil(totalCount / limit),
      },
    };
  }
}
