"""
BSS Skill: Legal Contract Summarizer
Extracts key information from legal contracts: parties, effective date,
term/duration, key obligations, payment terms, IP clauses, termination,
governing law. Reduces contract text by 85-95%.
"""
import re
from collections import OrderedDict

text = PROMPT  # injected by BIBIVIDI sandbox wrapper


# ── Party extraction ───────────────────────────────────────────────────────────

PARTY_RE = re.compile(
    r'(?:'
    # "between <Party A> and <Party B>"
    r'between\s+(.{3,80}?)\s+(?:\(".*?"\))?\s*and\s+(.{3,80}?)(?:\s+\(".*?"\))?\s*[,\.\(]'
    r'|entre\s+(.{3,80}?)\s+(?:\(".*?"\))?\s*et\s+(.{3,80}?)(?:\s+\(".*?"\))?\s*[,\.\(]'
    r')',
    re.IGNORECASE | re.DOTALL,
)

DEFINED_PARTY_RE = re.compile(
    r'([\w\s\.,&\-]+(?:Inc\.|LLC|Ltd\.?|S\.A\.S?\.?|SAS|SARL|GmbH|Corp\.?|Limited)?)'
    r'\s*\((?:hereinafter|ci-après|the\s+"?)([A-Z][^"]{2,30})(?:"|\))',
    re.IGNORECASE,
)


def extract_parties(txt):
    parties = OrderedDict()

    # Look for "between X and Y"
    m = PARTY_RE.search(txt)
    if m:
        if m.group(1) and m.group(2):
            parties['Party 1'] = m.group(1).strip()[:80]
            parties['Party 2'] = m.group(2).strip()[:80]
        elif m.group(3) and m.group(4):
            parties['Partie 1'] = m.group(3).strip()[:80]
            parties['Partie 2'] = m.group(4).strip()[:80]

    # Also extract defined party aliases: "Acme Corp ("Client")"
    aliases = []
    for m2 in DEFINED_PARTY_RE.finditer(txt):
        name  = m2.group(1).strip()
        alias = m2.group(2).strip().rstrip('")')
        if len(name) >= 3:
            aliases.append(f'{name} ("{alias}")')
    if aliases:
        parties['Defined parties'] = ' / '.join(aliases[:4])

    return parties


# ── Date parsing ───────────────────────────────────────────────────────────────

MONTH_MAP = {
    'january': 1, 'february': 2, 'march': 3, 'april': 4,
    'may': 5, 'june': 6, 'july': 7, 'august': 8,
    'september': 9, 'october': 10, 'november': 11, 'december': 12,
    'janvier': 1, 'février': 2, 'mars': 3, 'avril': 4,
    'mai': 5, 'juin': 6, 'juillet': 7, 'août': 8,
    'septembre': 9, 'octobre': 10, 'novembre': 11, 'décembre': 12,
}


def find_date(txt):
    """Return the first recognisable date string from txt."""
    # ISO date
    m = re.search(r'\d{4}-\d{2}-\d{2}', txt)
    if m:
        return m.group(0)
    # "15 January 2026" / "January 15, 2026" / "15 janvier 2026"
    m = re.search(
        r'(\d{1,2})\s+([a-zéû]{4,12})\s+(\d{4})'
        r'|([a-zéû]{4,12})\s+(\d{1,2})[,\s]+(\d{4})',
        txt, re.IGNORECASE,
    )
    if m:
        return m.group(0)
    # DD/MM/YYYY
    m = re.search(r'\d{1,2}[/.\-]\d{1,2}[/.\-]\d{4}', txt)
    if m:
        return m.group(0)
    return None


# ── Effective date / term ──────────────────────────────────────────────────────

EFF_DATE_RE = re.compile(
    r'(?:effective\s+(?:as\s+of\s+|date\s+of\s+)?|dated?\s+(?:as\s+of\s+)?'
    r'|entered\s+into\s+(?:as\s+of\s+)?|date\s+d\'effet\s*[:\-]?\s*|en\s+vigueur\s+le\s*)'
    r'([A-Za-zéû\s,\d/.\-]+?)(?=[,\.\n])',
    re.IGNORECASE,
)

TERM_RE = re.compile(
    r'(?:term\s+of\s+(?:this\s+)?agreement\s*[:\-]?\s*'
    r'|(?:initial\s+)?term\s*[:\-]?\s*'
    r'|durée\s*(?:du\s+contrat)?\s*[:\-]?\s*'
    r'|période\s*[:\-]?\s*)'
    r'([^\.\n]{5,80})',
    re.IGNORECASE,
)

DURATION_RE = re.compile(
    r'(?:for\s+a\s+(?:period|term)\s+of\s+|pour\s+une\s+durée\s+de\s+|d\'une\s+durée\s+de\s+)'
    r'(\d+\s+(?:year|month|day|an|mois|jour)s?)',
    re.IGNORECASE,
)


def extract_dates(txt):
    info = {}

    m = EFF_DATE_RE.search(txt)
    if m:
        d = find_date(m.group(1))
        info['Effective date'] = d or m.group(1).strip()[:40]

    m = TERM_RE.search(txt)
    if m:
        info['Term'] = m.group(1).strip()[:80]
    else:
        m = DURATION_RE.search(txt)
        if m:
            info['Duration'] = m.group(1).strip()

    return info


# ── Section extraction ────────────────────────────────────────────────────────

CLAUSE_HEADERS = [
    ('payment',          re.compile(
        r'^(?:\d+\.?\s+)?(?:payment|fees?|compensation|rémunération|paiement|prix|price|consideration)',
        re.IGNORECASE,
    )),
    ('ip',               re.compile(
        r'^(?:\d+\.?\s+)?(?:intellectual\s+property|ip\s+rights?|ownership|propriété\s+intellectuelle'
        r'|droits?\s+de\s+propriété)',
        re.IGNORECASE,
    )),
    ('confidentiality',  re.compile(
        r'^(?:\d+\.?\s+)?(?:confidential(?:ity)?|non[-\s]disclosure|nda|secret|confidentialité)',
        re.IGNORECASE,
    )),
    ('termination',      re.compile(
        r'^(?:\d+\.?\s+)?(?:terminat(?:ion|e)|expir|rescision|résiliation|résolution|fin\s+du\s+contrat)',
        re.IGNORECASE,
    )),
    ('governing_law',    re.compile(
        r'^(?:\d+\.?\s+)?(?:governing\s+law|jurisdiction|arbitrat|dispute|loi\s+applicable'
        r'|juridiction|litige|règlement\s+des\s+différends)',
        re.IGNORECASE,
    )),
    ('warranties',       re.compile(
        r'^(?:\d+\.?\s+)?(?:warrantie?s?|representations?|disclaimer|garantie|représentation)',
        re.IGNORECASE,
    )),
    ('liability',        re.compile(
        r'^(?:\d+\.?\s+)?(?:limit(?:ation)?\s+of\s+liability|liability|indemnif|responsabilité'
        r'|limitation\s+de\s+responsabilité)',
        re.IGNORECASE,
    )),
    ('obligations',      re.compile(
        r'^(?:\d+\.?\s+)?(?:obligation|duties?|responsibilities|scope\s+of\s+(?:work|services?)'
        r'|services?|prestation)',
        re.IGNORECASE,
    )),
]


def _is_clause_header(line):
    stripped = line.strip()
    # Short, ends in colon, is numbered, or is all-caps
    if len(stripped) > 80:
        return None
    stripped_clean = stripped.lstrip('0123456789. ').rstrip(':').strip()
    if not stripped_clean:
        return None
    for canon, pat in CLAUSE_HEADERS:
        if pat.match(stripped):
            return canon
    return None


def extract_clause_summaries(txt):
    """Return dict: clause_type → first 2 sentences of the clause body."""
    clauses = OrderedDict()
    current_type = None
    buffer = []

    for line in txt.splitlines():
        stripped = line.strip()
        htype = _is_clause_header(stripped) if stripped else None

        if htype:
            if current_type and buffer:
                body = ' '.join(buffer)
                # Take first 1-2 sentences
                sentences = re.split(r'(?<=[.!?])\s+', body)
                clauses[current_type] = ' '.join(sentences[:2])[:250]
            current_type = htype
            buffer = []
        elif current_type and stripped:
            buffer.append(stripped)
            # Stop after 10 lines per clause to avoid over-capture
            if len(buffer) >= 10:
                body = ' '.join(buffer)
                sentences = re.split(r'(?<=[.!?])\s+', body)
                clauses[current_type] = ' '.join(sentences[:2])[:250]
                current_type = None
                buffer = []

    # Flush last clause
    if current_type and buffer:
        body = ' '.join(buffer)
        sentences = re.split(r'(?<=[.!?])\s+', body)
        clauses[current_type] = ' '.join(sentences[:2])[:250]

    return clauses


# ── Payment terms quick extract ───────────────────────────────────────────────

PAYMENT_RE = re.compile(
    r'(?:payment\s+(?:terms?|due|of)|fees?\s+of|amount\s+of|total\s+(?:price|fee|amount)'
    r'|montant|prix\s+(?:total|de\s+la\s+prestation)|frais\s+de)'
    r'\s*[:\-]?\s*'
    r'((?:€|\$|£)?\s*[\d\s,\.]+(?:€|\$|£)?(?:\s*(?:k|K|m|M))?(?:\s*/\s*(?:month|year|an|mois))?)',
    re.IGNORECASE,
)


def extract_payment_terms(txt):
    terms = []
    for m in PAYMENT_RE.finditer(txt):
        val = m.group(0).strip()[:120]
        if val not in terms:
            terms.append(val)
        if len(terms) >= 3:
            break
    return terms


# ── Governing law quick extract ───────────────────────────────────────────────

GOV_LAW_RE = re.compile(
    r'(?:governed\s+by\s+(?:the\s+laws?\s+of\s+)?|subject\s+to\s+(?:the\s+laws?\s+of\s+)?'
    r'|jurisdiction\s+of\s+|loi\s+(?:française|applicable|de\s+)(?:l\'état\s+)?)'
    r'([^\.\n]{3,60})',
    re.IGNORECASE,
)


def extract_governing_law(txt):
    m = GOV_LAW_RE.search(txt)
    if m:
        return m.group(0).strip()[:100]
    return None


# ── Contract type detection ───────────────────────────────────────────────────

CONTRACT_TYPE_PATTERNS = [
    ('Service Agreement',    re.compile(r'\b(?:service\s+agreement|services?\s+contract|contrat\s+de\s+prestation)\b', re.I)),
    ('NDA',                  re.compile(r'\b(?:non[\s\-]disclosure|confidentiality\s+agreement|NDA|accord\s+de\s+confidentialité)\b', re.I)),
    ('Employment Contract',  re.compile(r'\b(?:employment\s+(?:agreement|contract)|contrat\s+de\s+travail|offer\s+letter)\b', re.I)),
    ('License Agreement',    re.compile(r'\b(?:license\s+agreement|licence\s+(?:agreement|contrat)|software\s+license)\b', re.I)),
    ('Partnership Agreement',re.compile(r'\b(?:partnership|joint\s+venture|partenariat)\b', re.I)),
    ('Purchase Agreement',   re.compile(r'\b(?:purchase\s+agreement|sale\s+(?:and\s+purchase|agreement)|contrat\s+(?:de\s+)?vente)\b', re.I)),
    ('Lease Agreement',      re.compile(r'\b(?:lease\s+agreement|tenancy|bail|contrat\s+de\s+location)\b', re.I)),
]


def detect_contract_type(txt):
    for label, pat in CONTRACT_TYPE_PATTERNS:
        if pat.search(txt):
            return label
    return None


# ── Main ───────────────────────────────────────────────────────────────────────

contract_type = detect_contract_type(text)
parties       = extract_parties(text)
date_info     = extract_dates(text)
clauses       = extract_clause_summaries(text)
payment_terms = extract_payment_terms(text)
governing_law = extract_governing_law(text)

lines = ['=== BIBIVIDI: CONTRACT SUMMARY ===']

if contract_type:
    lines.append(f'Type    : {contract_type}')

for k, v in parties.items():
    lines.append(f'{k:<15}: {v}')

for k, v in date_info.items():
    lines.append(f'{k:<15}: {v}')

if governing_law:
    lines.append(f'Governing law  : {governing_law}')

if payment_terms:
    lines.append('\n[PAYMENT TERMS]')
    for t in payment_terms:
        lines.append(f'  {t}')

CLAUSE_LABELS = {
    'obligations':     'Obligations',
    'payment':         'Payment',
    'ip':              'Intellectual Property',
    'confidentiality': 'Confidentiality',
    'warranties':      'Warranties',
    'liability':       'Liability',
    'termination':     'Termination',
    'governing_law':   'Governing Law',
}

if clauses:
    lines.append('\n[KEY CLAUSES]')
    for key, label in CLAUSE_LABELS.items():
        if key in clauses:
            summary = clauses[key]
            lines.append(f'  {label}:')
            lines.append(f'    {summary}')

if not parties and not clauses and not date_info:
    # Nothing structural found — pass through beginning of document
    lines.append('\n[RAW EXCERPT]')
    lines.append(text[:600])

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

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