"""
BSS Skill: Job Posting Parser
Parses a job posting from PROMPT and outputs a structured compact summary:
title, company, location, contract, salary, experience, requirements,
responsibilities, perks, and application instructions.
"""
import re

text = PROMPT  # injected by BIBIVIDI sandbox wrapper

# ── Section splitting ──────────────────────────────────────────────────────────

SECTION_PATTERNS = {
    'requirements':      r'(?:requirements?|required|compétences? requises?|must[\s-]have|requis|qualifications?|what you(?:\'ll)? bring|profil recherché|what we(?:\'re)? looking for)',
    'responsibilities':  r'(?:responsibilities|missions?|your role|votre rôle|what you(?:\'ll)? do|what you will do|the role|le poste|job description|about the role)',
    'perks':             r'(?:benefits?|perks?|avantages?|what we offer|ce que nous offrons|what you(?:\'ll)? get|we offer|nous offrons|compensation|package)',
    'apply':             r'(?:how to apply|application|postuler|apply now|candidature|to apply)',
}


def split_sections(txt):
    """Return a dict {section_name: text_block}."""
    pattern = re.compile(
        r'^(?:#+\s*|\*{1,2}\s*)?(' + '|'.join(SECTION_PATTERNS.values()) + r')\s*[*:.\-–]?\s*$',
        re.IGNORECASE | re.MULTILINE,
    )
    sections = {k: '' for k in SECTION_PATTERNS}
    boundaries = [(m.start(), m.end(), m.group(1)) for m in pattern.finditer(txt)]

    for i, (start, end, header) in enumerate(boundaries):
        next_start = boundaries[i + 1][0] if i + 1 < len(boundaries) else len(txt)
        block = txt[end:next_start].strip()
        for key, pat in SECTION_PATTERNS.items():
            if re.match(pat, header, re.IGNORECASE):
                sections[key] += '\n' + block
                break

    return sections


# ── Metadata extractors ────────────────────────────────────────────────────────

TITLE_KEYWORDS = (
    r'engineer|manager|developer|analyst|designer|architect|lead|director|'
    r'chef|directeur|ingénieur|développeur|consultant|officer|specialist|'
    r'responsable|coordinat|head of|vp |vice.president|product owner|'
    r'data scientist|devops|sre|scrum|full.?stack|front.?end|back.?end'
)


def get_title(txt):
    """Job title from the first meaningful line or lines containing role keywords."""
    lines = txt.splitlines()

    # Try first non-empty short line (likely the posting headline)
    for line in lines[:6]:
        line = line.strip().strip('#').strip('*').strip()
        if not line or len(line) < 4:
            continue
        if re.search(TITLE_KEYWORDS, line, re.IGNORECASE):
            return line[:100]
        if len(line) <= 80 and not re.search(r'@|http|www|\.com', line, re.IGNORECASE):
            return line[:100]

    # Fallback: scan for a line with a role keyword
    for line in lines:
        line = line.strip().strip('#').strip('*').strip()
        if 4 <= len(line) <= 100 and re.search(TITLE_KEYWORDS, line, re.IGNORECASE):
            return line[:100]

    return ''


def get_company(txt):
    """Company name from explicit labels or prominent short lines near the title."""
    # Explicit labels
    m = re.search(
        r'(?:company|entreprise|société|employer|organisation)\s*[:\-–]\s*([^\n]{2,60})',
        txt[:1200], re.IGNORECASE,
    )
    if m:
        return m.group(1).strip()

    # "at <Company>" / "chez <Company>" patterns
    m = re.search(
        r'(?:\bat\b|chez)\s+([A-Z][A-Za-zÀ-ÿ0-9&.\-\s]{1,40})',
        txt[:800],
    )
    if m:
        return m.group(1).strip().rstrip('.,')

    return ''


def get_location(txt):
    """City/country and remote/hybrid/on-site keywords."""
    parts = []

    # Explicit location labels (highest confidence)
    loc_m = re.search(
        r'(?:location|lieu|localisation|lieu de travail|based in|situ[eé][e]? [aà])\s*[:\-–]\s*([^\n]{2,60})',
        txt[:1500], re.IGNORECASE,
    )
    if loc_m:
        city = loc_m.group(1).strip().rstrip('.,')
        parts.append(city)
    else:
        # Look for "City, Country" after a "|" or "•" separator — avoids matching role keywords
        city_m = re.search(
            r'[|•·]\s*([A-ZÀ-Ÿ][a-zà-ÿ]{2,20}(?:[\s\-][A-ZÀ-Ÿ][a-zà-ÿ]{2,20})?'
            r'(?:,\s*[A-ZÀ-Ÿ][a-zà-ÿ]{2,20}(?:\s+[A-ZÀ-Ÿ]{2,3})?)?)\b',
            txt[:400],
        )
        if city_m:
            parts.append(city_m.group(1).strip())

    # Remote / hybrid / on-site keywords
    remote_m = re.search(
        r'\b(remote|full[\s-]remote|t[eé]l[eé]travail|hybrid|hybride|on[\s-]site|pr[eé]sentiel|distanciel)\b',
        txt[:2000], re.IGNORECASE,
    )
    if remote_m:
        label = remote_m.group(1)
        if label.lower() not in [p.lower() for p in parts]:
            parts.append(label)

    return ' | '.join(parts) if parts else ''


def get_salary(txt):
    """Salary range — keyword-led lines first, then currency-anchored patterns."""
    # Keyword-led salary lines (most reliable — returns the whole value description)
    m = re.search(
        r'(?:salary|r[eé]mun[eé]ration|salaire|package|ctc|compensation)\s*[:\-–]\s*([^\n]{2,80})',
        txt, re.IGNORECASE,
    )
    if m:
        return m.group(1).strip()[:80]

    # Currency-anchored range: "70-90k€", "€70k - €90k", "70 000 EUR"
    m = re.search(
        r'(?:[\$€£]\s*)?\d[\d\s]*[k]?\s*(?:[-–]\s*(?:[\$€£]\s*)?\d[\d\s]*[k]?\s*)?'
        r'(?:EUR|USD|GBP|CHF|k€|€|\$|£)',
        txt, re.IGNORECASE,
    )
    if m:
        # Walk back to start of the line for context
        line_start = txt.rfind('\n', 0, m.start()) + 1
        line_end = txt.find('\n', m.end())
        if line_end == -1:
            line_end = len(txt)
        return txt[line_start:line_end].strip()[:80]

    return ''


def get_experience(txt):
    """Years of experience from common patterns."""
    m = re.search(
        r"(\d+)\s*\+?\s*(?:years?|ans?|ann\xe9es?)\s*(?:of\s+)?(?:experience|d['\u2019]exp\xe9rience)",
        txt, re.IGNORECASE,
    )
    if m:
        return m.group(0).strip()

    m = re.search(
        r'(?:minimum|at least|au moins|minimum de)\s*(\d+)\s*(?:years?|ans?)',
        txt, re.IGNORECASE,
    )
    if m:
        return m.group(0).strip()

    return ''


def get_contract(txt):
    """Contract type: CDI, CDD, freelance, permanent, contract, internship, etc."""
    m = re.search(
        r'\b(CDI|CDD|freelance|free[\s-]lance|contrat|permanent|contract(?:or)?|'
        r'internship|intern|stage|alternance|apprentissage|part[\s-]time|full[\s-]time|'
        r'temps partiel|temps plein)\b',
        txt[:2000], re.IGNORECASE,
    )
    return m.group(1) if m else ''


# ── Section parsers ────────────────────────────────────────────────────────────

def _parse_bullets(block, max_items, max_len):
    """Extract bullet items from a text block."""
    items = []
    for line in block.splitlines():
        line = re.sub(r'^[\s\-•·*▪▸►→✓✔]+', '', line).strip()
        if len(line) < 5:
            continue
        # Skip all-caps section headers
        if re.match(r'^[A-Z][A-Z\s]{4,}[:\-]?\s*$', line):
            continue
        # Skip metadata key: value lines (e.g. "Salary: 70k€", "Apply at: jobs@")
        if re.match(r'^[A-Za-zÀ-ÿ][A-Za-zÀ-ÿ\s]{2,25}[:\-–]\s*\S', line):
            continue
        items.append(line[:max_len])
        if len(items) >= max_items:
            break
    return items


def _parse_section_fallback(txt, header_pattern, max_items, max_len):
    """Parse items from a section using a header pattern when split_sections misses it."""
    m = re.search(
        r'(?:' + header_pattern + r')\s*[*:.\-–]?\s*\n((?:.+\n?){1,20})',
        txt, re.IGNORECASE | re.MULTILINE,
    )
    if m:
        return _parse_bullets(m.group(1), max_items, max_len)
    return []


def parse_requirements(txt, sections):
    """Required skills list, max 15 items."""
    block = sections.get('requirements', '')
    if block.strip():
        return _parse_bullets(block, 15, 90)
    return _parse_section_fallback(
        txt,
        r'requirements?|required|compétences? requises?|must[\s-]have|requis|qualifications?|profil recherché',
        15, 90,
    )


def parse_responsibilities(txt, sections):
    """Job responsibilities, max 8 items truncated to 80 chars."""
    block = sections.get('responsibilities', '')
    if block.strip():
        return _parse_bullets(block, 8, 80)
    return _parse_section_fallback(
        txt,
        r'responsibilities|missions?|your role|votre rôle|what you(?:\'ll)? do|le poste',
        8, 80,
    )


def parse_perks(txt, sections):
    """Perks/benefits, max 6 items."""
    block = sections.get('perks', '')
    if block.strip():
        return _parse_bullets(block, 6, 90)
    return _parse_section_fallback(
        txt,
        r'benefits?|perks?|avantages?|what we offer|ce que nous offrons|we offer|nous offrons',
        6, 90,
    )


def get_apply(txt, sections):
    """Application instructions — email, URL, or 'apply via' text."""
    block = sections.get('apply', '').strip()

    # Email in the apply block or full text
    email_m = re.search(r'[\w.+-]+@[\w.-]+\.\w{2,}', block or txt[-800:])
    if email_m:
        return email_m.group(0)[:100]

    # URL in the apply block
    if block:
        url_m = re.search(r'https?://[\S]{5,80}', block)
        if url_m:
            return url_m.group(0)[:100]
        # First non-empty line of the apply block
        for line in block.splitlines():
            line = line.strip()
            if len(line) > 8:
                return line[:100]

    # Inline "apply via / send to" patterns
    m = re.search(
        r'(?:apply\s+(?:via|at|to|on)|send\s+(?:cv|resume|candidature)\s+(?:to|à)|postuler\s+(?:via|sur|à))\s*[:\-–]?\s*([^\n]{5,100})',
        txt, re.IGNORECASE,
    )
    if m:
        return m.group(1).strip()[:100]

    # Application URL anywhere in the text
    url_m = re.search(r'https?://[\S]{5,80}', txt[-1000:])
    if url_m:
        return url_m.group(0)[:100]

    return ''


# ── Parse & output ─────────────────────────────────────────────────────────────

sections = split_sections(text)

title       = get_title(text)
company     = get_company(text)
location    = get_location(text)
salary      = get_salary(text)
experience  = get_experience(text)
contract    = get_contract(text)
reqs        = parse_requirements(text, sections)
resps       = parse_responsibilities(text, sections)
perks       = parse_perks(text, sections)
apply_info  = get_apply(text, sections)

lines = ['=== BIBIVIDI: JOB POSTING ===']
if title:
    lines.append(f'Title   : {title}')
if company:
    lines.append(f'Company : {company}')
if location:
    lines.append(f'Location: {location}')
if contract:
    lines.append(f'Contract: {contract}')
if salary:
    lines.append(f'Salary  : {salary}')
if experience:
    lines.append(f'Exp     : {experience}')

if reqs:
    lines.append('\n[REQUIREMENTS]')
    for r in reqs:
        lines.append(f'  - {r}')

if resps:
    lines.append('\n[RESPONSIBILITIES]')
    for r in resps:
        lines.append(f'  - {r}')

if perks:
    lines.append('\n[PERKS]')
    for p in perks:
        lines.append(f'  - {p}')

if apply_info:
    lines.append('\n[APPLY]')
    lines.append(f'  {apply_info}')

lines.append(f'\n=== END (condensed from {len(text)} chars) ===')

print('\n'.join(lines))
