import re
from typing import List
from transformers import pipeline
from sentence_transformers import SentenceTransformer, util

# 1) Load required models (lazy load)
_zeroshot_classifier = None
_ner_pipeline = None
_sentence_model = None

def get_zeroshot_classifier():
    global _zeroshot_classifier
    if _zeroshot_classifier is None:
        _zeroshot_classifier = pipeline(
            "zero-shot-classification",
            model="facebook/bart-large-mnli"
        )
    return _zeroshot_classifier

def get_ner_pipeline():
    global _ner_pipeline
    if _ner_pipeline is None:
        _ner_pipeline = pipeline(
            "ner",
            model="Jean-Baptiste/roberta-large-ner-english",
            tokenizer="Jean-Baptiste/roberta-large-ner-english",
            aggregation_strategy="simple"
        )
    return _ner_pipeline

def get_sentence_model():
    global _sentence_model
    if _sentence_model is None:
        _sentence_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
    return _sentence_model


# 2) Extended skill set across domains (example, expand as needed)
EXTENDED_SKILLS = [
    # Programming & IT
    "Python", "Java", "JavaScript", "TypeScript", "SQL", "HTML", "CSS",
    "React", "Django", "Flask", "FastAPI", "Node.js", "MongoDB", "MySQL",
    "PostgreSQL", "Firebase", "Git", "Docker", "Linux", "TensorFlow",
    "Pandas", "NumPy", "Scikit-learn", "Kubernetes", "Azure", "GCP", "AWS",
    "Machine Learning", "Deep Learning", "NLP","C","PHP"

    # Data Science & Analytics
    "Tableau", "Power BI", "Excel", "SAS", "SPSS", "R", "Data Analysis",
    "Big Data", "Hadoop", "Spark",

    # Marketing & Sales
    "SEO", "Google Ads", "Content Marketing", "Social Media",
    "Google Analytics", "Email Marketing", "CRM", "Salesforce",

    # Business & Management
    "Project Management", "Agile", "Scrum", "Kanban", "Six Sigma",
    "Risk Management", "Business Analysis",

    # Finance & Accounting
    "Financial Modeling", "QuickBooks", "SAP", "Budgeting",
    "Investment Banking", "Taxation", "Auditing",

    # Engineering & Manufacturing
    "CAD", "MATLAB", "SolidWorks", "AutoCAD", "Lean Manufacturing",
    "Quality Control", "Process Improvement",

    # Healthcare
    "Patient Care", "EMR", "HIPAA", "Clinical Trials", "Pharmacy",

    # Design & Creative
    "Adobe Photoshop", "Illustrator", "UI/UX Design", "Sketch",
    "InDesign", "After Effects",

]

# Context labels to filter skill-related text
SKILL_CONTEXT_LABELS = [
    "Skills",
    "Technical skills",
    "Professional skills",
    "Core competencies",
    "Tools and technologies",
    "Languages",
    "Software",
    "Work experience",
    "Education",
    "Certifications",
    "Compétences",

]


def preprocess_text(text: str) -> str:
    text = re.sub(r'\S+@\S+', '', text)
    text = re.sub(r'\b\d{8,}\b', '', text)
    text = re.sub(r'https?://\S+', '', text)
    return text.strip()


def extract_skills(cv_text: str, threshold_bert=0.4, threshold_ner=0.75, threshold_regex=0.7) -> List[str]:
    """
    Hybrid skill extraction combining zero-shot context filtering,
    NER entity extraction with semantic similarity,
    and regex matching against an extended skills list.
    """

    cleaned_text = preprocess_text(cv_text)
    zeroshot = get_zeroshot_classifier()
    ner = get_ner_pipeline()
    sentence_model = get_sentence_model()

    # Step 1: Zero-shot classification to check if text is skill-related
    cls_res = zeroshot(cleaned_text[:512], candidate_labels=SKILL_CONTEXT_LABELS)
    top_label = cls_res["labels"][0].lower()
    if all(x not in top_label for x in ["skill", "competencies", "technologies", "tools"]):
        # Not skill-related context, return empty list early
        return []

    # Step 2: Extract candidate entities from NER
    ner_entities = ner(cleaned_text)
    ner_candidates = {ent['word'] for ent in ner_entities if ent['entity_group'] in ['MISC', 'ORG', 'PRODUCT']}

    # Encode extended skills and detected entities for similarity comparison
    skill_embeddings = sentence_model.encode(EXTENDED_SKILLS, convert_to_tensor=True)
    ner_embeddings = sentence_model.encode(list(ner_candidates), convert_to_tensor=True) if ner_candidates else []

    # Compute cosine_sim and keep matches above threshold_ner
    skills_from_ner = set()
    if ner_candidates:
        import torch
        cosine_scores = util.cos_sim(ner_embeddings, skill_embeddings)
        for i, ner_ent in enumerate(ner_candidates):
            scores = cosine_scores[i]
            max_score, max_idx = torch.max(scores, dim=0)
            if max_score >= threshold_ner:
                skills_from_ner.add(EXTENDED_SKILLS[max_idx])

    # Step 3: Regex matching as fallback / complement for explicit mentions
    skills_from_regex = set()
    for skill in EXTENDED_SKILLS:
        pattern = r'\b' + re.escape(skill) + r'\b'
        if re.search(pattern, cleaned_text, flags=re.IGNORECASE):
            skills_from_regex.add(skill)

    # Step 4: Combine findings (union)
    final_skills = skills_from_ner.union(skills_from_regex)

    # Return sorted list
    return sorted(final_skills)

make it works also with french cvs