"""Whisper (faster-whisper large-v3) + pyannote çıktılarını birleştirir.

Girdi: <T>/liste.json, <T>/cikti/*.json (kelime zamanlı metin), <T>/diar/*.json (konuşmacı parçaları), <T>/ses/*.flac
Çıktı: Transkript/ klasörü
  - <Bölüm>.txt              okunur metin, saat + konuşmacı
  - <Bölüm>_kelimeler.json   her kelime: saat (gece yarısından beri sn), kaynak, konuşmacı
  - kaynak/<dosya>.srt       her kaynak dosyaya göre (dosyanın başı = 0) altyazı

Birleştirme kuralları:
  - Aynı (ya da sadece eki farklı) kelime ±1 sn içinde Mik 1 ve Mik 2'de varsa, konuşmacı ayrımının o mikrofonun sahibi dediği
    kopya kalır (Mik 2 = eğitmen, Mik 1 = seyirci mikrofonu); karar verilemezse Mik 2 kopyası.
  - Eğitim'de Mik 1'in kaydı olmayan aralıkta Mik 2'si çıkarılmış seyirci sesi (seyirci_ayir.py), o da
    yoksa Kamera A sesi (Mik 1+2 karışık) kullanılır; Mik 2'de
    zaten olan kelimeler atılır, kalanlar seyirci konuşmasıdır.
  - Sohbet'te 23:00 öncesi sadece Kamera B sesi var.
Konuşmacı etiketleri dosya başınadır (M2-S03 = Mik 2 dosyasında 3. kişi); dosyalar arası aynı kişi
aynı etiketi almayabilir. Mik 2'de en çok konuşan "EĞİTMEN" olarak adlandırılır.
"""
import bisect, difflib, glob, json, os, re, subprocess, sys
import numpy as np

T = sys.argv[1]
OUT = "/home/stark/Desktop/Eğitim - 01/Transkript"
os.makedirs(f"{OUT}/kaynak", exist_ok=True)
KISA = {"Mikrofon 1": "M1", "Mikrofon 2": "M2", "Kamera A sesi": "KA", "Kamera B sesi": "KB", "Seyirci (ayrılmış)": "SA"}


def saat(t, ms=False):
    t = max(t, 0)
    s = f"{int(t) // 3600:02d}:{int(t) % 3600 // 60:02d}:{int(t) % 60:02d}"
    return s + f",{int(round((t % 1) * 1000)) % 1000:03d}" if ms else s


def norm(w):
    return re.sub(r"[^\wçğıöşüâîû]", "", w.lower())


def benzer(a, b):
    """Aynı kelime mi: birebir, ya da ek farkı (ör. vurdu/vurdum, duvarlarına/duvarlarının)."""
    if a == b: return True
    if min(len(a), len(b)) < 4: return False
    return a[:5] == b[:5] or difflib.SequenceMatcher(None, a, b).ratio() >= 0.8


liste = json.load(open(f"{T}/liste.json"))
kaynaklar = []
for l in liste:
    c = json.load(open(f"{T}/cikti/{l['ad']}.json"))
    dp = f"{T}/diar/{l['ad']}.json"
    D = json.load(open(dp)) if os.path.exists(dp) else []
    Db = [d[0] for d in D]
    kel = []
    for s in c["segment"]:
        for b, e, w, p in s["kelime"]:
            m = (b + e) / 2; spk = None
            i = bisect.bisect_right(Db, m) - 1                       # ortası hangi parçadaysa
            for j in range(max(i - 3, 0), min(i + 3, len(D))):
                if D[j][0] <= m <= D[j][1]:
                    spk = D[j][2]
            if spk is None and D:                                     # parçalar arası boşluk: en yakını
                j = min(range(max(i - 1, 0), min(i + 2, len(D))), key=lambda j: min(abs(D[j][0] - m), abs(D[j][1] - m)))
                spk = D[j][2]
            kel.append(dict(b=b, e=e, w=w.strip(), p=p, spk=f"{KISA[l['iz']]}-S{spk[-2:]}" if spk else f"{KISA[l['iz']]}-?"))
    kaynaklar.append(dict(l, kel=kel))

# Mik 2 dosyalarında en çok konuşan = eğitmen (Eğitim) ; Sohbet'te her mikrofonun sahibi
for k in kaynaklar:
    if k["iz"] in ("Mikrofon 1", "Mikrofon 2") and k["kel"]:
        say = {}
        for w in k["kel"]:
            say[w["spk"]] = say.get(w["spk"], 0) + 1
        sahip = max(say, key=say.get)
        ad = ("EĞİTMEN" if k["iz"] == "Mikrofon 2" else "SEYİRCİ MİK.") if k["bolum"] == "Eğitim" else f"{KISA[k['iz']]} SAHİBİ"
        for w in k["kel"]:
            if w["spk"] == sahip:
                w["spk"] = ad


# --- kaynak başına SRT (dosya göreli)
def satirlar(kel, en_uzun=7.0, en_cok=84):
    out, cur = [], []
    for w in kel:
        if cur and (w["spk"] != cur[-1]["spk"] or w["b"] - cur[-1]["e"] > 0.8 or w["e"] - cur[0]["b"] > en_uzun
                    or len(" ".join(x["w"] for x in cur)) + len(w["w"]) > en_cok):
            out.append(cur); cur = []
        cur.append(w)
    if cur:
        out.append(cur)
    return out


for k in kaynaklar:
    with open(f"{OUT}/kaynak/{k['ad']}.srt", "w", encoding="utf-8") as f:
        for i, s in enumerate(satirlar(k["kel"]), 1):
            f.write(f"{i}\n{saat(s[0]['b'], True)} --> {saat(s[-1]['e'], True)}\n[{s[0]['spk']}] {' '.join(x['w'] for x in s)}\n\n")


SAHIP = {"Mikrofon 1": ("SEYİRCİ MİK.", "M1 SAHİBİ"), "Mikrofon 2": ("EĞİTMEN", "M2 SAHİBİ")}

for bolum in ("Eğitim", "Sohbet"):
    K = [k for k in kaynaklar if k["bolum"] == bolum]
    m1 = [k for k in K if k["iz"] == "Mikrofon 1"]; m2 = [k for k in K if k["iz"] == "Mikrofon 2"]
    # Aynı konuşmayı iki mikrofon da duyar. Aynı kelime ±1 sn içinde iki mikrofonda varsa:
    # konuşmacı ayrımının o mikrofonun sahibi dediği kopya kalır; karar verilemezse Mik 2 kopyası.
    # Kopyası atılan kelimelerin arasında kalan, sahibi olmayan tekil kelimeler de atılır (cümle bölünmesin).
    def liste_(grup):
        L = []
        for k in grup:
            for w in k["kel"]:
                L.append(dict(w, t0=k["bas"] + w["b"], t1=k["bas"] + w["e"], kaynak=k["ad"], iz=k["iz"], at=False, kopya=False))
        return sorted(L, key=lambda w: w["t0"])
    W1, W2 = liste_(m1), liste_(m2)
    t2 = [w["t0"] for w in W2]; eslesti = set()
    for w in W1:
        nw = norm(w["w"])
        if not nw: continue
        i = bisect.bisect_left(t2, w["t0"] - 1.0); en = None
        while i < len(W2) and W2[i]["t0"] <= w["t0"] + 1.0:
            if i not in eslesti and benzer(norm(W2[i]["w"]), nw) and (en is None or abs(W2[i]["t0"] - w["t0"]) < abs(W2[en]["t0"] - w["t0"])):
                en = i
            i += 1
        if en is None: continue
        eslesti.add(en); v = W2[en]
        w["kopya"] = v["kopya"] = True
        if w["spk"] in SAHIP["Mikrofon 1"] and v["spk"] not in SAHIP["Mikrofon 2"]:
            v["at"] = True
        else:
            w["at"] = True
    for L, iz in ((W1, "Mikrofon 1"), (W2, "Mikrofon 2")):
        for i, w in enumerate(L):
            if w["at"] or w["kopya"] or w["spk"] in SAHIP[iz]: continue
            komsu = L[max(i - 2, 0):i] + L[i + 1:i + 3]
            if sum(x["at"] for x in komsu) >= 2:
                w["at"] = True
    sil = [w for w in W1 + W2 if w["at"]]
    tum = [w for w in W1 + W2 if not w["at"]]
    # Eğitim: Mik 1 kaydı olmayan aralıkta Kamera A sesi; Mik 2'de olan kelimeler atılır
    if bolum == "Eğitim":
        m1_araliklar = [(k["bas"], k["bas"] + k["sure"]) for k in m1]
        m2k = sorted((w["t0"], norm(w["w"])) for w in tum if w["iz"] == "Mikrofon 2")
        m2t = [a for a, _ in m2k]
        # Önce Mik 2'si çıkarılmış seyirci sesi (seyirci_ayir.py); olmayan yerde karışık Kamera A sesi
        ayrilmis = [(k["bas"], k["bas"] + k["sure"]) for k in K if k["iz"] == "Seyirci (ayrılmış)"]
        for k in K:
            if k["iz"] not in ("Kamera A sesi", "Seyirci (ayrılmış)"): continue
            for w in k["kel"]:
                t = k["bas"] + w["b"]
                if any(a <= t <= b for a, b in m1_araliklar): continue
                if k["iz"] == "Kamera A sesi" and any(a <= t <= b for a, b in ayrilmis): continue
                i = bisect.bisect_left(m2t, t - 0.6)          # kalan eğitmen izi yazıya dökülmüşse at
                if any(benzer(m2k[j][1], norm(w["w"])) for j in range(i, min(i + 12, len(m2k))) if m2k[j][0] <= t + 0.6): continue
                spk = f"SEYİRCİ {w['spk']}" if k["iz"] == "Seyirci (ayrılmış)" else "SEYİRCİ (Kamera A)"
                tum.append(dict(w, t0=t, t1=k["bas"] + w["e"], kaynak=k["ad"], iz=k["iz"], spk=spk))
    # Sohbet: 23:00 öncesi sadece Kamera B
    if bolum == "Sohbet":
        for k in K:
            if k["iz"] == "Kamera B sesi":
                for w in k["kel"]:
                    tum.append(dict(w, t0=k["bas"] + w["b"], t1=k["bas"] + w["e"], kaynak=k["ad"], iz=k["iz"]))
    tum.sort(key=lambda w: w["t0"])
    json.dump([dict(saat=round(w["t0"], 2), bitis=round(w["t1"], 2), kelime=w["w"], konusmaci=w["spk"], kaynak=w["kaynak"],
                    olasilik=w["p"]) for w in tum], open(f"{OUT}/{bolum}_kelimeler.json", "w"), ensure_ascii=False, indent=0)
    # okunur metin: konuşmacı değişince ya da 2 sn sessizlikte yeni paragraf
    with open(f"{OUT}/{bolum}.txt", "w", encoding="utf-8") as f:
        f.write(f"{bolum} — transkript (saatler gerçek saat; konuşmacı etiketleri dosya başına, bkz. transkript_birlestir.py)\n\n")
        cur = []
        for w in tum + [None]:
            if cur and (w is None or w["spk"] != cur[-1]["spk"] or w["t0"] - cur[-1]["t1"] > 2.0):
                f.write(f"[{saat(cur[0]['t0'])}] {cur[0]['spk']}: {' '.join(x['w'] for x in cur)}\n")
                cur = []
            if w: cur.append(w)
    print(bolum, len(tum), "kelime;", len(sil), "kelime öbür mikrofondaki kopyası tutulduğu için atıldı")
