"""
BSS Skill: Support Ticket Analyzer
Parses support ticket batches (CSV, JSON-like, or text lists) and outputs
priority distribution, category breakdown, escalated/critical tickets,
and most common issue themes. Reduces ticket data by 75-90%.
"""
import csv
import io
import re
from collections import defaultdict

text = PROMPT  # injected by BIBIVIDI sandbox wrapper


# ── Priority normalisation ─────────────────────────────────────────────────────

PRIORITY_MAP = [
    ('CRITICAL', re.compile(r'\b(?:critical|critique|p0|sev(?:erity)?[\s\-]?0|urgent|blocker|bloquant)\b', re.I)),
    ('HIGH',     re.compile(r'\b(?:high|élevé|p1|sev(?:erity)?[\s\-]?1)\b', re.I)),
    ('MEDIUM',   re.compile(r'\b(?:medium|moyen|p2|sev(?:erity)?[\s\-]?2|normal)\b', re.I)),
    ('LOW',      re.compile(r'\b(?:low|faible|p3|sev(?:erity)?[\s\-]?3|minor|mineur)\b', re.I)),
]


def normalize_priority(raw):
    if not raw:
        return 'UNKNOWN'
    for label, pat in PRIORITY_MAP:
        if pat.search(raw):
            return label
    return raw.strip().upper()[:10]


# ── Status normalisation ───────────────────────────────────────────────────────

STATUS_MAP = [
    ('Open',        re.compile(r'\b(?:open|ouvert|new|nouveau|unassigned|non[\s\-]assigné)\b', re.I)),
    ('In Progress', re.compile(r'\b(?:in[\s\-]?progress|en\s+cours|assigned|assigné|working|investigation)\b', re.I)),
    ('Pending',     re.compile(r'\b(?:pending|en\s+attente|waiting|wait|blocked)\b', re.I)),
    ('Resolved',    re.compile(r'\b(?:resolved?|résolu|fixed|corrigé|closed|fermé|done|terminé)\b', re.I)),
    ('Escalated',   re.compile(r'\b(?:escalat|escalade|urgence)\b', re.I)),
]


def normalize_status(raw):
    if not raw:
        return 'Unknown'
    for label, pat in STATUS_MAP:
        if pat.search(raw):
            return label
    return raw.strip().title()[:20]


# ── Category / theme detection ────────────────────────────────────────────────

CATEGORY_PATTERNS = [
    ('Login / Auth',     re.compile(r'\b(?:login|auth(?:entication)?|password|sign[\s\-]in|connexion|mot\s+de\s+passe|access)\b', re.I)),
    ('Performance',      re.compile(r'\b(?:slow|performance|latency|latence|timeout|lag|loading|chargement|speed)\b', re.I)),
    ('Billing',          re.compile(r'\b(?:billing|invoice|payment|charge|facture|paiement|subscription|abonnement)\b', re.I)),
    ('Data / Export',    re.compile(r'\b(?:data|export|import|download|télécharg|csv|pdf|report|rapport)\b', re.I)),
    ('UI / Display',     re.compile(r'\b(?:ui|display|layout|page|button|form|affichage|interface|broken|cassé|visual)\b', re.I)),
    ('Integration',      re.compile(r'\b(?:api|webhook|integration|sync|connect|third[\s\-]party|externe|third\s+party)\b', re.I)),
    ('Error / Crash',    re.compile(r'\b(?:error|crash|exception|500|bug|broken|fail|panne|erreur|plantage)\b', re.I)),
    ('Feature Request',  re.compile(r'\b(?:feature|request|enhancement|amélioration|new\s+feature|demande)\b', re.I)),
    ('Onboarding',       re.compile(r'\b(?:onboard|setup|install|configuration|getting\s+started|démarrage)\b', re.I)),
]


def infer_category(title_or_body):
    t = (title_or_body or '').lower()
    for label, pat in CATEGORY_PATTERNS:
        if pat.search(t):
            return label
    return 'Other'


# ── CSV parsing ───────────────────────────────────────────────────────────────

def _try_parse_csv(txt):
    """Try to read as CSV. Returns list of ticket dicts or empty list."""
    lines = txt.splitlines()
    # Need at least a header + 1 data row
    csv_like = [l for l in lines if l.count(',') >= 2 or l.count(';') >= 2 or l.count('\t') >= 2]
    if len(csv_like) < 2:
        return []

    # Detect delimiter
    first = csv_like[0]
    if first.count('\t') >= 2:
        delim = '\t'
    elif first.count(';') > first.count(','):
        delim = ';'
    else:
        delim = ','

    try:
        reader = csv.DictReader(io.StringIO('\n'.join(csv_like)), delimiter=delim)
        rows = list(reader)
        headers = reader.fieldnames or []
    except Exception:
        return []

    if not rows:
        return []

    def find_col(patterns):
        for pat in patterns:
            for h in headers:
                if re.search(pat, h, re.IGNORECASE):
                    return h
        return None

    col_id       = find_col([r'\bid\b|ticket[\s\#]?(?:id|no|num|#)|#|\bnum\b'])
    col_title    = find_col([r'title|subject|summary|résumé|titre|description'])
    col_priority = find_col([r'priority|priorité|severity|sévérité|urgency'])
    col_status   = find_col([r'status|statut|state|état'])
    col_category = find_col([r'categor|type|label|tag|component'])
    col_assignee = find_col([r'assignee|assigned|agent|owner|responsable'])
    col_created  = find_col([r'creat|opened|submitted|date'])

    tickets = []
    for row in rows:
        def cell(c):
            return row.get(c, '').strip() if c else ''

        tid      = cell(col_id)
        title    = cell(col_title)
        priority = normalize_priority(cell(col_priority))
        status   = normalize_status(cell(col_status))
        category = cell(col_category) or infer_category(title)
        assignee = cell(col_assignee)
        created  = cell(col_created)

        if not title and not tid:
            continue

        tickets.append({
            'id':       tid[:20],
            'title':    title[:120],
            'priority': priority,
            'status':   status,
            'category': category[:40],
            'assignee': assignee[:40],
            'created':  created[:20],
        })

    return tickets


# ── Text / bullet parsing ─────────────────────────────────────────────────────

TICKET_LINE_RE = re.compile(
    r'(?:'
    r'(?:#\s*|ticket[\s\-]?)(?P<id>\d+)\s*[:\-]?\s*'
    r'|(?P<idstr>[A-Z]{2,6}[\s\-]\d+)\s*[:\-]?\s*'
    r')?'
    r'(?P<title>[^\[<(]{5,100}?)'
    r'(?:'
    r'\s*[\[\(<](?P<prio>[A-Za-z\d\-]+)[\]\)>]'
    r'|\s*[-–|]\s*(?P<prio2>[A-Za-z\d\-\s]+?)\s*(?:[-–|]|$)'
    r')?'
    r'\s*$',
    re.IGNORECASE,
)


def _try_parse_text(txt):
    """Parse bullet-list / numbered-list ticket descriptions."""
    tickets = []
    for line in txt.splitlines():
        line = line.strip()
        if not line or len(line) < 10:
            continue
        # Skip pure headers
        if re.match(r'^(?:tickets?|issues?|support|summary|résumé)\s*[:\-]?\s*$', line, re.I):
            continue
        # Remove bullet chars
        line_clean = re.sub(r'^[-•*\d.]+\s*', '', line).strip()
        if len(line_clean) < 8:
            continue

        # Extract inline priority/status hints
        prio_m = re.search(r'\[([^\]]{2,15})\]|\(([^)]{2,15})\)', line_clean)
        prio_raw = (prio_m.group(1) or prio_m.group(2)) if prio_m else ''

        priority = normalize_priority(prio_raw) if prio_raw else normalize_priority(line_clean)
        status   = normalize_status(line_clean)
        category = infer_category(line_clean)

        # Title = clean line without trailing brackets
        title = re.sub(r'\s*[\[\(][^\]\)]{2,15}[\]\)]\s*$', '', line_clean).strip()

        tickets.append({
            'id':       '',
            'title':    title[:120],
            'priority': priority,
            'status':   status,
            'category': category,
            'assignee': '',
            'created':  '',
        })

    return tickets


def parse_tickets(txt):
    tickets = _try_parse_csv(txt)
    if not tickets:
        tickets = _try_parse_text(txt)
    return tickets


# ── Aggregation ───────────────────────────────────────────────────────────────

def priority_distribution(tickets):
    dist = defaultdict(int)
    for t in tickets:
        dist[t['priority']] += 1
    order = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'UNKNOWN']
    return [(p, dist[p]) for p in order if dist[p] > 0]


def status_distribution(tickets):
    dist = defaultdict(int)
    for t in tickets:
        dist[t['status']] += 1
    return sorted(dist.items(), key=lambda x: x[1], reverse=True)


def category_distribution(tickets):
    dist = defaultdict(int)
    for t in tickets:
        dist[t['category']] += 1
    return sorted(dist.items(), key=lambda x: x[1], reverse=True)[:8]


def critical_tickets(tickets):
    return [t for t in tickets if t['priority'] in ('CRITICAL', 'HIGH')][:5]


def escalated_tickets(tickets):
    return [t for t in tickets if t['status'] == 'Escalated'][:5]


# ── Aggregate stats from free-text when no structured tickets ─────────────────

def extract_text_stats(txt):
    """Pull aggregate numbers mentioned in the text."""
    stats = {}

    # Total count: "127 tickets", "45 open issues"
    m = re.search(r'(\d+)\s+(?:tickets?|issues?|cases?|demandes?|open)', txt, re.I)
    if m:
        stats['total_mentioned'] = int(m.group(1))

    # SLA breach: "12 SLA breaches"
    m = re.search(r'(\d+)\s+(?:sla\s+breach|sla\s+violation|sla\s+miss)', txt, re.I)
    if m:
        stats['sla_breaches'] = int(m.group(1))

    # Average resolution time
    m = re.search(r'avg(?:erage)?\s+(?:resolution|response)\s+time\s*[:\-]?\s*([^\n.]{3,40})', txt, re.I)
    if m:
        stats['avg_resolution'] = m.group(1).strip()

    # CSAT / satisfaction score
    m = re.search(r'(?:csat|satisfaction|score)\s*[:\-]?\s*(\d+(?:\.\d+)?)\s*(?:%|/\d+)?', txt, re.I)
    if m:
        stats['csat'] = m.group(0).strip()[:40]

    return stats


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

tickets = parse_tickets(text)
text_stats = extract_text_stats(text)

lines = ['=== BIBIVIDI: SUPPORT TICKETS ===']

if tickets:
    lines.append(f'Total    : {len(tickets)} tickets')

    prio_dist = priority_distribution(tickets)
    if prio_dist:
        lines.append('\n[PRIORITY DISTRIBUTION]')
        for prio, count in prio_dist:
            bar = '█' * min(count, 20)
            lines.append(f'  {prio:<10}: {count:>3}  {bar}')

    status_dist = status_distribution(tickets)
    if status_dist:
        lines.append('\n[STATUS]')
        for status, count in status_dist:
            lines.append(f'  {status:<15}: {count}')

    cat_dist = category_distribution(tickets)
    if cat_dist:
        lines.append('\n[TOP CATEGORIES]')
        for cat, count in cat_dist:
            lines.append(f'  {cat:<20}: {count}')

    critical = critical_tickets(tickets)
    if critical:
        lines.append('\n[CRITICAL / HIGH PRIORITY]')
        for t in critical:
            tid = f'#{t["id"]} ' if t['id'] else ''
            lines.append(f'  [{t["priority"]}] {tid}{t["title"]}')

    escalated = escalated_tickets(tickets)
    if escalated:
        lines.append('\n[ESCALATED]')
        for t in escalated:
            tid = f'#{t["id"]} ' if t['id'] else ''
            lines.append(f'  {tid}{t["title"]}')

elif text_stats:
    lines.append('\n[AGGREGATE STATS FROM TEXT]')
    for k, v in text_stats.items():
        lines.append(f'  {k}: {v}')

else:
    # Nothing parseable
    lines.append(text[:600])

if text_stats:
    if 'sla_breaches' in text_stats:
        lines.append(f'\nSLA breaches : {text_stats["sla_breaches"]}')
    if 'avg_resolution' in text_stats:
        lines.append(f'Avg resolution: {text_stats["avg_resolution"]}')
    if 'csat' in text_stats:
        lines.append(f'CSAT: {text_stats["csat"]}')

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

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