#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
map_wn16_to_wn30.py — versión 2
- bag en formato semiestructurado (no JSON)
- detección de merges → merge.csv

Salida:
  rough_mapping.csv : pos,synset_Id16,bag
  no_map.csv        : pos,synset_Id16,bag=no_map
  split.csv         : pos,synset_Id16,bag (varios destinos)
  mapping_WN16_to_WN30.csv : Index,synset_Id16,synset_Id30,pos
  merge.csv         : Index,pos,synset_Id30,synset_Id16_list
"""

import csv
import argparse
import sys
from collections import defaultdict

# ------------------------------------------------------------
# Utilidades
# ------------------------------------------------------------

def ss_type_to_pos(ss_type):
    return {"1": "n", "2": "v", "3": "a", "4": "r", "5": "s"}.get(ss_type, "?")

def synset_id_from_offset(offset, ss_type):
    base = {"1": 100000000, "2": 200000000, "3": 300000000, "4": 400000000, "5": 300000000}[ss_type]
    return base + int(offset)

def parse_index_sense_line(line):
    parts = line.strip().split()
    if len(parts) < 4:
        return None
    sense_key, synset_offset, sense_number, tag_cnt = parts[:4]
    # lemma and ss_type from sense_key
    if '%' not in sense_key:
        return None
    lemma = sense_key.split('%')[0]
    ss_type = sense_key.split('%')[1].split(':')[0]
    pos = ss_type_to_pos(ss_type)
    return lemma, ss_type, pos, synset_offset

# ------------------------------------------------------------
# Cargar index_sense30 → sense_key → synset_Id30
# ------------------------------------------------------------

def load_index_sense30(path):
    """Carga index_sense30 con parseo robusto (avisa líneas inválidas)."""
    mapping = {}
    with open(path, 'r', encoding='utf8') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            parts = line.split()
            if len(parts) < 2:
                print(f"[WARN] Línea malformada en index_sense30: {line}", file=sys.stderr)
                continue
            sense_key = parts[0]
            offset = parts[1]
            if not offset.isdigit():
                print(f"[WARN] Offset inválido en index_sense30: {offset}  | línea: {line}", file=sys.stderr)
                continue
            synset_offset = offset  # keep as string (consistent with rest of code)
            # extract ss_type
            try:
                ss_type = sense_key.split('%',1)[1].split(':',1)[0]
            except Exception:
                print(f"[WARN] sense_key malformado en index_sense30: {sense_key}  | línea: {line}", file=sys.stderr)
                continue
            syn_id = synset_id_from_offset(synset_offset, ss_type)
            lemma = sense_key.split('%',1)[0]
            mapping[sense_key] = (syn_id, lemma)
    return mapping
    mapping = {}
    with open(path, 'r', encoding='utf8') as f:
        for line in f:
            if not line.strip() or line.strip().startswith('#'):
                continue
            parsed = parse_index_sense_line(line)
            if parsed:
                lemma, ss_type, pos, synset_offset = parsed
                syn_id = synset_id_from_offset(synset_offset, ss_type)
                # reconstruir sense_key completo
                sense_key = line.split()[0]
                mapping[sense_key] = (syn_id, lemma)
    return mapping

# ------------------------------------------------------------
# Procesar index_sense16 → rough_mapping
# ------------------------------------------------------------

def process_index_sense16(path, map30):
    # Estructura: pos,synset_Id16 → { synset_Id30: set(lemas) }
    bag = defaultdict(lambda: defaultdict(set))

    with open(path, 'r', encoding='utf8') as f:
        for line in f:
            if not line.strip() or line.strip().startswith('#'):
                continue
            parsed = parse_index_sense_line(line)
            if not parsed:
                continue
            lemma, ss_type, pos, synset_offset = parsed
            syn16 = synset_id_from_offset(synset_offset, ss_type)
            sense_key = line.split()[0]

            if sense_key in map30:
                syn30, lemma30 = map30[sense_key]
                bag[(pos, syn16)][syn30].add(lemma)
            else:
                # No mapea → asegúrate de que la clave existe (bag vacío)
                _ = bag[(pos, syn16)]
    return bag

# ------------------------------------------------------------
# Escritura y formatos
# ------------------------------------------------------------

def bag_to_semistructured(bag_dict):
    # bag_dict: { synset_Id30 : set(lemas) }
    if not bag_dict:
        return ""
    parts = []
    for syn30 in sorted(bag_dict.keys()):
        lemmas = sorted(bag_dict[syn30])
        parts.append(f"{syn30}:" + "|".join(lemmas))
    return " ; ".join(parts)

# ------------------------------------------------------------
# Detección de merges
# ------------------------------------------------------------

def detect_merges(bag):
    # bag: (pos,syn16) → { syn30: set(lemas) }
    inv = defaultdict(list)  # (pos,syn30) → list of syn16
    for (pos, syn16), d in bag.items():
        for syn30 in d.keys():
            inv[(pos, syn30)].append(syn16)
    # Filtrar los que tienen más de un synset16
    merges = { (pos, syn30): sorted(lst) for (pos, syn30), lst in inv.items() if len(lst) > 1 }
    return merges

# ------------------------------------------------------------
# Main
# ------------------------------------------------------------

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--wn16', required=True)
    ap.add_argument('--wn30', required=True)
    ap.add_argument('--out-prefix', default="")
    args = ap.parse_args()

    p = args.out_prefix

    map30 = load_index_sense30(args.wn30)
    bag = process_index_sense16(args.wn16, map30)

    # --- rough_mapping.csv ---
    with open(p + "rough_mapping.csv", 'w', newline='', encoding='utf8') as f:
        w = csv.writer(f)
        w.writerow(["pos", "synset_Id16", "bag"])
        for (pos, syn16) in sorted(bag.keys(), key=lambda x: (x[0], x[1])):
            b = bag_to_semistructured(bag[(pos, syn16)])
            w.writerow([pos, syn16, b])

    # --- Clasificación y escritura de mapping / no_map / split ---
    count_map = 0
    count_split = 0
    count_no = 0

    with open(p + "mapping_WN16_to_WN30.csv", 'w', newline='', encoding='utf8') as f_map:
        with open(p + "no_map.csv", 'w', newline='', encoding='utf8') as f_no:
            with open(p + "split.csv", 'w', newline='', encoding='utf8') as f_split:

                w_map = csv.writer(f_map)
                w_no = csv.writer(f_no)
                w_split = csv.writer(f_split)

                w_map.writerow(["Index", "synset_Id16", "synset_Id30", "pos"])
                w_no.writerow(["pos", "synset_Id16", "bag"])
                w_split.writerow(["pos", "synset_Id16", "bag"])

                index = 1
                for (pos, syn16) in sorted(bag.keys(), key=lambda x: (x[0], x[1])):
                    d = bag[(pos, syn16)]
                    if not d:
                        w_no.writerow([pos, syn16, "no_map"])
                        count_no += 1
                    elif len(d) == 1:
                        syn30 = next(iter(d.keys()))
                        w_map.writerow([index, syn16, syn30, pos])
                        index += 1
                        count_map += 1
                    else:
                        w_split.writerow([pos, syn16, bag_to_semistructured(d)])
                        count_split += 1

    # --- merges ---
    merges = detect_merges(bag)
    with open(p + "merge.csv", 'w', newline='', encoding='utf8') as f_m:
        w = csv.writer(f_m)
        w.writerow(["Index", "pos", "synset_Id30", "synset_Id16_list"])
        idx = 1
        for (pos, syn30), lst in sorted(merges.items(), key=lambda x: (x[0][0], x[0][1])):
            syn16_list = "|".join(str(x) for x in lst)
            w.writerow([idx, pos, syn30, syn16_list])
            idx += 1

    # --- estadísticas ---
    print("==== ESTADÍSTICAS ====")
    print(f"Mapeos 1→1: {count_map}")
    print(f"Splits: {count_split}")
    print(f"No map: {count_no}")
    print(f"Merges: {len(merges)}")


if __name__ == '__main__':
    main()
