import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JobApplications } from '../job_applications/entities/job_applications.entity';
import { JobPosts } from '../job_posts/entities/job_posts.entity';

interface SeekerProfile {
  user_id: number;
  seeker_id: number;
  name: string;
  email: string;
  professional_title?: string;
  biography?: string;
  experience_id?: number;
  education_id?: number;
  skills?: any[];
  education?: any[];
  experience?: any[];
  role_id?: number;
  role_name?: string;
  industry_id?: number;
  location?: string;
}

export interface RankedCandidate extends SeekerProfile {
  score: number;
  matchDetails: {
    skillsMatch: number;
    educationMatch: number;
    experienceMatch: number;
    roleMatch: number;
  };
}

@Injectable()
export class AiScreeningService {
  constructor(
    @InjectRepository(JobApplications)
    private jobApplicationsRepository: Repository<JobApplications>,
    @InjectRepository(JobPosts)
    private jobPostsRepository: Repository<JobPosts>,
  ) {}

  /**
   * Calculate weighted score for a candidate based on job requirements
   * Weightage system:
   * - Skills: 40%
   * - Experience: 30%
   * - Education: 20%
   * - Role: 10%
   */
  private calculateCandidateScore(
    candidate: SeekerProfile,
    jobRequirements: any,
    weightage: any = {
      skills: 40,
      experience: 25,
      education: 10,
      role: 15,
      company_count: 10,
    },
  ): { score: number; matchDetails: any } {
    let totalScore = 0;
    const matchDetails = {
      skillsMatch: 0,
      educationMatch: 0,
      experienceMatch: 0,
      roleMatch: 0,
      companyCountMatch: 0,
    };

    // Skills Match (40% weight)
    if (jobRequirements.required_skills && candidate.skills) {
      // console.log(`[Screening Debug] JobID: ${jobRequirements.id}, SeekerID: ${candidate.seeker_id}`);
      // console.log(`[Screening Debug] Job Skills (Raw):`, jobRequirements.required_skills);
      // console.log(`[Screening Debug] Candidate Skills (Raw):`, candidate.skills);

      const jobSkills = Array.isArray(jobRequirements.required_skills)
        ? jobRequirements.required_skills
            .map((s: any) => {
              const skillName =
                typeof s === 'string' ? s : s.name || s.skill_name || '';
              return skillName.toLowerCase().trim();
            })
            .filter((s) => s) // Remove empty
        : [];

      const candidateSkills = Array.isArray(candidate.skills)
        ? candidate.skills
            .map((s: any) => {
              const skillName =
                typeof s === 'string' ? s : s.skill_name || s.name || '';
              return skillName.toLowerCase().trim();
            })
            .filter((s) => s)
        : [];

      // console.log(`[Screening Debug] Normalized Job Skills:`, jobSkills);
      // console.log(`[Screening Debug] Normalized Candidate Skills:`, candidateSkills);

      if (jobSkills.length > 0) {
        const matchingSkills = candidateSkills.filter((skill) =>
          jobSkills.includes(skill),
        );
        // console.log(`[Screening Debug] Matching Skills:`, matchingSkills);
        matchDetails.skillsMatch =
          (matchingSkills.length / jobSkills.length) * 100;
        totalScore += matchDetails.skillsMatch * (weightage.skills / 100);
      }
    }

    // Experience Match (25% weight)
    if (jobRequirements.experience_id && candidate.experience_id) {
      // Exact match gets 100%, adjacent levels get 70%, others get 30%
      const experienceDiff = Math.abs(
        jobRequirements.experience_id - candidate.experience_id,
      );
      if (experienceDiff === 0) {
        matchDetails.experienceMatch = 100;
      } else if (experienceDiff === 1) {
        matchDetails.experienceMatch = 70;
      } else {
        matchDetails.experienceMatch = 30;
      }
      totalScore += matchDetails.experienceMatch * (weightage.experience / 100);
    }

    // Education Match (10% weight)
    if (jobRequirements.education_id && candidate.education_id) {
      // Higher education than required gets 100%, exact match gets 100%, lower gets 50%
      if (candidate.education_id >= jobRequirements.education_id) {
        matchDetails.educationMatch = 100;
      } else {
        matchDetails.educationMatch = 50;
      }
      totalScore += matchDetails.educationMatch * (weightage.education / 100);
    }

    // Role Match (15% weight)
    if (jobRequirements.role_id) {
      const jobRoleId = Number(jobRequirements.role_id);
      const candidateRoleId = Number(candidate.role_id);

      // console.log(`[Screening Debug] Job Role ID: ${jobRoleId}, Candidate Role ID: ${candidateRoleId}`);

      let roleMatchScore = 0;

      if (candidateRoleId && jobRoleId === candidateRoleId) {
        roleMatchScore = 100;
      } else {
        // Fallback: Match by Role Name (if available)
        const jobRoleName = (jobRequirements.role_name || '')
          .toLowerCase()
          .trim();
        const candRoleName = (candidate.role_name || '').toLowerCase().trim();
        const candTitle = (candidate.professional_title || '')
          .toLowerCase()
          .trim();

        // console.log(`[Screening Debug] Role Names - Job: "${jobRoleName}", CandRole: "${candRoleName}", CandTitle: "${candTitle}"`);

        if (jobRoleName && candRoleName && jobRoleName === candRoleName) {
          roleMatchScore = 100;
        } else if (
          jobRoleName &&
          candTitle &&
          (candTitle.includes(jobRoleName) || jobRoleName.includes(candTitle))
        ) {
          roleMatchScore = 70;
        } else if (
          jobRoleName &&
          candRoleName &&
          (candRoleName.includes(jobRoleName) ||
            jobRoleName.includes(candRoleName))
        ) {
          roleMatchScore = 70;
        }
      }
      matchDetails.roleMatch = roleMatchScore;
      totalScore += matchDetails.roleMatch * (weightage.role / 100);
    }

    // Stability / Company Count (10% default)
    // 1-2 companies -> 100 (High stability)
    // 3-4 companies -> 70
    // 5+ companies -> 40
    const companyCount = candidate.experience?.length || 0;
    if (companyCount > 0) {
      if (companyCount <= 2) matchDetails.companyCountMatch = 100;
      else if (companyCount <= 4) matchDetails.companyCountMatch = 70;
      else matchDetails.companyCountMatch = 40;
    } else {
      // No experience listed, assume neutral or 0?
      // If experience_id exists but no rows, might be incomplete profile.
      // Let's give a default small score or 0.
      matchDetails.companyCountMatch = 50;
    }
    totalScore +=
      matchDetails.companyCountMatch * (weightage.company_count / 100);

    return { score: Math.round(totalScore), matchDetails };
  }

  /**
   * Screen and rank applicants for a specific job
   */
  async screenApplicants(
    jobId: number,
    weightage?: any,
  ): Promise<RankedCandidate[]> {
    // Get job details with requirements (Role Name + Skills)
    const jobRaw = await this.jobPostsRepository.query(
      `
            SELECT 
                jp.*, 
                r.name as role_name,
                (
                    SELECT GROUP_CONCAT(s.skill_name) 
                    FROM job_post_skills jps 
                    JOIN skills s ON jps.skill_id = s.id 
                    WHERE jps.job_post_id = jp.id
                ) as required_skills
            FROM job_posts jp 
            LEFT JOIN roles r ON jp.role_id = r.id 
            WHERE jp.id = ?
        `,
      [jobId],
    );

    const job = jobRaw[0];

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

    // Parse skills if string
    if (job.required_skills && typeof job.required_skills === 'string') {
      job.required_skills = job.required_skills.split(',');
    } else {
      job.required_skills = [];
    }

    // Get all applicants for this job
    const applications = await this.jobApplicationsRepository
      .createQueryBuilder('app')
      .where('app.job_post_id = :jobId', { jobId })
      .getMany();

    if (applications.length === 0) {
      return [];
    }

    // Create a map of seeker_id to application_id
    const seekerAppMap = new Map<number, number>();
    applications.forEach((app) => seekerAppMap.set(app.seeker_id, app.id));

    // Get seeker profiles for all applicants
    const seekerIds = applications.map((app) => app.seeker_id);
    const seekerProfiles = await this.getSeekerProfiles(seekerIds);

    // Calculate scores for each candidate
    const rankedCandidates: RankedCandidate[] = seekerProfiles.map(
      (candidate) => {
        const { score, matchDetails } = this.calculateCandidateScore(
          candidate,
          job,
          weightage,
        );
        return {
          ...candidate,
          application_id: seekerAppMap.get(candidate.seeker_id), // Add application_id
          score,
          matchDetails,
        };
      },
    );

    // Sort by score (highest first)
    rankedCandidates.sort((a, b) => b.score - a.score);

    return rankedCandidates;
  }

  /**
   * Find top candidates from the entire database for a job
   */
  async findTopCandidates(
    jobId: number,
    count: number = 10,
    filters?: {
      experience?: string;
      location?: string;
      skills?: string;
    },
    weightage?: any,
  ): Promise<RankedCandidate[]> {
    // Get job details with requirements (Role Name + Skills)
    const jobRaw = await this.jobPostsRepository.query(
      `
            SELECT 
                jp.*, 
                r.name as role_name,
                (
                    SELECT GROUP_CONCAT(s.skill_name) 
                    FROM job_post_skills jps 
                    JOIN skills s ON jps.skill_id = s.id 
                    WHERE jps.job_post_id = jp.id
                ) as required_skills
            FROM job_posts jp 
            LEFT JOIN roles r ON jp.role_id = r.id 
            WHERE jp.id = ?
        `,
      [jobId],
    );

    const job = jobRaw[0];

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

    // Parse skills if string
    if (job.required_skills && typeof job.required_skills === 'string') {
      job.required_skills = job.required_skills.split(',');
    } else {
      job.required_skills = [];
    }

    // Get all seeker profiles (with optional filters)
    let query = `
      SELECT DISTINCT
        u.id as user_id,
        sp.id as seeker_id,
        u.full_name as name,
        u.email,
        sp.professional_title,
        sp.bio_graphy as biography,
        sp.experiences_id as experience_id,
        sp.education_id,
        sp.profession_id as role_id,
        r.name as role_name
      FROM seeker_profiles sp
      INNER JOIN users u ON sp.user_id = u.id
      LEFT JOIN roles r ON sp.profession_id = r.id
      WHERE u.role = 'seeker' AND u.status = 'active'
      AND NOT EXISTS (
        SELECT 1 FROM job_applications ja 
        WHERE ja.seeker_id = sp.id 
        AND ja.job_post_id = ?
      )
    `;

    const params: any[] = [jobId]; // Add jobId as first param for the NOT EXISTS clause
    let paramIndex = 1;

    // Apply filters if provided
    if (filters?.experience) {
      const expMap: any = {
        entry: 1,
        mid: 2,
        senior: 3,
        expert: 4,
      };
      const expId = expMap[filters.experience.toLowerCase()];
      if (expId) {
        query += ` AND sp.experiences_id = ?`;
        params.push(expId);
      }
    }

    if (filters?.location) {
      query += ` AND EXISTS (
        SELECT 1 FROM seeker_address sa
        WHERE sa.seeker_profile_id = sp.id
        AND (sa.permanent_city_id IN (SELECT id FROM city WHERE name LIKE ?)
             OR sa.permanent_state_id IN (SELECT id FROM state WHERE name LIKE ?))
      )`;
      params.push(`%${filters.location}%`);
      params.push(`%${filters.location}%`);
    }

    query += ` LIMIT ${Math.min(count * 3, 300)}`; // Get more than needed for better filtering

    const seekerProfiles = await this.jobPostsRepository.query(query, params);

    // Get skills for each seeker
    const seekersWithSkills = await Promise.all(
      seekerProfiles.map(async (seeker: any) => {
        const skills = await this.jobPostsRepository.query(
          `
          SELECT s.skill_name
          FROM seeker_skills ss
          INNER JOIN skills s ON ss.skill_id = s.id
          WHERE ss.seeker_id = ?
        `,
          [seeker.seeker_id],
        );

        const education = await this.jobPostsRepository.query(
          `
          SELECT se.degree, se.year, el.name as education_level
          FROM seeker_education se
          LEFT JOIN education_levels el ON se.education_id = el.id
          WHERE se.seeker_id = ?
          ORDER BY se.year DESC
          LIMIT 1
        `,
          [seeker.seeker_id],
        );

        return {
          ...seeker,
          skills: skills.map((s: any) => s.skill_name),
          education,
        };
      }),
    );

    // Fetch experience count for each seeker for stability score
    const seekersWithFullData = await Promise.all(
      seekersWithSkills.map(async (seeker: any) => {
        const experiences = await this.jobPostsRepository.query(
          `SELECT id FROM seeker_experience WHERE seeker_id = ?`,
          [seeker.seeker_id],
        );
        return { ...seeker, experience: experiences };
      }),
    );

    // Apply skill filter if provided
    let filteredSeekers = seekersWithFullData;
    if (filters?.skills) {
      const requiredSkills = filters.skills
        .split(',')
        .map((s) => s.trim().toLowerCase());
      filteredSeekers = seekersWithSkills.filter((seeker) => {
        const seekerSkills = (seeker.skills || []).map((s: string) =>
          s.toLowerCase(),
        );
        return requiredSkills.some((reqSkill) =>
          seekerSkills.some((seekerSkill) => seekerSkill.includes(reqSkill)),
        );
      });
    }

    // Calculate scores for each candidate
    const rankedCandidates: RankedCandidate[] = filteredSeekers.map(
      (candidate) => {
        const { score, matchDetails } = this.calculateCandidateScore(
          candidate,
          job,
          weightage,
        );
        return {
          ...candidate,
          score,
          matchDetails,
        };
      },
    );

    // Sort by score (highest first) and return top N
    rankedCandidates.sort((a, b) => b.score - a.score);

    return rankedCandidates.slice(0, count);
  }

  /**
   * Helper method to get seeker profiles with all related data
   */
  private async getSeekerProfiles(
    seekerIds: number[],
  ): Promise<SeekerProfile[]> {
    if (seekerIds.length === 0) return [];

    const placeholders = seekerIds.map(() => '?').join(',');

    const profiles = await this.jobPostsRepository.query(
      `
      SELECT DISTINCT
        u.id as user_id,
        sp.id as seeker_id,
        u.full_name as name,
        u.email,
        sp.professional_title,
        sp.bio_graphy as biography,
        sp.experiences_id as experience_id,
        sp.education_id,
        sp.profession_id as role_id,
        r.name as role_name
      FROM seeker_profiles sp
      INNER JOIN users u ON sp.user_id = u.id
      LEFT JOIN roles r ON sp.profession_id = r.id
      WHERE sp.id IN (${placeholders})
    `,
      seekerIds,
    );

    // Get skills for each seeker
    const profilesWithSkills = await Promise.all(
      profiles.map(async (profile: any) => {
        const skills = await this.jobPostsRepository.query(
          `
          SELECT s.skill_name
          FROM seeker_skills ss
          INNER JOIN skills s ON ss.skill_id = s.id
          WHERE ss.seeker_id = ?
        `,
          [profile.seeker_id],
        );

        return {
          ...profile,
          skills: skills.map((s: any) => s.skill_name),
        };
      }),
    );

    // Fetch experience count for stability score
    const finalProfiles = await Promise.all(
      profilesWithSkills.map(async (profile: any) => {
        const experiences = await this.jobPostsRepository.query(
          `SELECT id FROM seeker_experience WHERE seeker_id = ?`,
          [profile.seeker_id],
        );
        return { ...profile, experience: experiences };
      }),
    );

    return finalProfiles;
  }
}
