import os, shutil, fdb, time, threading
import re, datetime, funkce, funkce_ext

# Connect to the Firebird database
conn = fdb.connect(
    dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
    user='SYSDBA',
    password="masterkey",
    charset="win1250")

cesta           = r"u:\testimport"
cestazpracovana = r"u:\testimportzpracovana"

# Konstanty pro detekci sekce Vložené přílohy (RTF kódování win1250)
PRILOHY_HEADER  = r"Vlo\'9een\'e9 p\'f8\'edlohy:"
PRILOHY_CLOSING = r'\pard\s10\plain\cs15\f0\fs20 \par'

# ─── Helper funkce ────────────────────────────────────────────────────────────

def restore_files_for_import(retezec):
    drop = r"u:\Dropbox\!!!Days\Downloads Z230\Dokumentace"
    next = r"u:\NextcloudOrdinace\Dokumentace_ke_zpracování"
    if not os.path.exists(drop):
        print(f"The directory '{drop}' does not exist.")
        return
    for item in os.listdir(drop):
        item_path = os.path.join(drop, item)
        if os.path.isfile(item_path) or os.path.islink(item_path):
            os.unlink(item_path)
            print(f"Deleted file: {item_path}")
        elif os.path.isdir(item_path):
            shutil.rmtree(item_path)
            print(f"Deleted directory: {item_path}")
    for item in os.listdir(next):
        item_path = os.path.join(next, item)
        if os.path.isfile(item_path) and item_path.endswith(".pdf") and retezec in item_path:
            shutil.copy(item_path, os.path.join(drop, item))
            print(f"Copied file: {item_path}")


def kontrola_rc(rc, connection):
    cur = connection.cursor()
    cur.execute("select count(*),idpac from kar where rodcis=? group by idpac", (rc,))
    row = cur.fetchone()
    if row:
        return row[1]
    else:
        return False


def kontrola_struktury(souborname, connection):
    if souborname.endswith('.pdf'):
        pattern = re.compile(r'(^\d{9,10}) (\d{4}-\d{2}-\d{2}) (\w+, \w.+?) \[(.+?)\] \[(.*?)\]')
        match = pattern.search(souborname)
        vpohode = True
        if match and len(match.groups()) == 5:
            datum = match.group(2)
            try:
                datetime.datetime.strptime(datum, "%Y-%m-%d").date()
            except:
                vpohode = False
                return vpohode
            cur = connection.cursor()
            cur.execute("select count(*) from kar where rodcis=?", (match.group(1),))
            row = cur.fetchone()[0]
            if row != 1:
                vpohode = False
                return vpohode
        else:
            vpohode = False
            return vpohode
    else:
        vpohode = False
        return vpohode
    return vpohode


def vrat_info_o_souboru(souborname, connection):
    pattern = re.compile(r'(^\d{9,10}) (\d{4}-\d{2}-\d{2}) (\w+, \w.+?) \[(.+?)\] \[(.*?)\]')
    match = pattern.search(souborname)
    rc           = match.group(1)
    datum        = datetime.datetime.strptime(match.group(2), "%Y-%m-%d").date()
    jmeno        = match.group(3)
    prvnizavorka = match.group(4)
    druhazavorka = match.group(5)
    cur = connection.cursor()
    cur.execute("select idpac from kar where rodcis=?", (rc,))
    idpac = cur.fetchone()[0]
    datumsouboru = datetime.datetime.fromtimestamp(os.path.getctime(os.path.join(cesta, souborname)))
    return (rc, idpac, datum, jmeno, prvnizavorka, druhazavorka, souborname, datumsouboru)


def prejmenuj_chybny_soubor(souborname, cesta):
    if souborname[0] != "♥":
        soubornovy = "♥" + souborname
        os.rename(os.path.join(cesta, souborname), os.path.join(cesta, soubornovy))


def _pokus_o_zamek(dekurs_id, vysledek):
    """Běží ve vlákně: pokusí se zamknout dekurz přes separátní spojení.
    Výsledek zapíše do slovníku vysledek: {'ok': True} nebo {'chyba': str}.
    Pokud vlákno stále běží po uplynutí timeoutu → záznam je zamčený.
    """
    conn_t = None
    try:
        conn_t = fdb.connect(
            dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
            user='SYSDBA', password='masterkey', charset='win1250'
        )
        cur_t = conn_t.cursor()
        cur_t.execute(
            "SELECT ID FROM DEKURS WHERE ID = ? FOR UPDATE WITH LOCK",
            (dekurs_id,)
        )
        cur_t.fetchone()
        conn_t.rollback()   # Uvolni zámek – sloužil jen k ověření
        vysledek['ok'] = True
    except Exception as e:
        vysledek['chyba'] = str(e)
    finally:
        if conn_t:
            try:
                conn_t.close()
            except Exception:
                pass


def zkus_zamknout_dnesni_dekurs(conn, idpac, datum_vlozeni, timeout_sec=2):
    """Zjistí zda existuje dnešní dekurz a ověří že není zamčený.

    Vrátí:
      (id, rtf)  – dnešní dekurz existuje a není zamčený
      None       – žádný dnešní dekurz (bude se dělat INSERT, zámek není potřeba)

    Vyhodí RuntimeError pokud je záznam zamčený jiným uživatelem (Medicus ho má otevřený).

    Poznámka: NOWAIT transakci fdb neumí spolehlivě nastavit, proto spustíme
    pokus o zámek ve vlákně s timeoutem. Pokud vlákno do timeout_sec sekund
    neskončí, záznam je zamčený a přeskočíme celou skupinu.
    """
    cur = conn.cursor()

    # Krok 1: přečti ID, datum a obsah posledního dekurzu (běžný SELECT)
    cur.execute("""
        SELECT FIRST 1 ID, DATUM, DEKURS FROM DEKURS
        WHERE IDPAC = ?
        ORDER BY ID DESC
    """, (idpac,))
    row = cur.fetchone()
    if row is None:
        print(f"    Žádný dekurz pro pacienta IDPAC={idpac}")
        return None

    dekurs_id, dekurs_datum, dekurs_rtf = row
    print(f"    Poslední dekurs: ID={dekurs_id}, datum={dekurs_datum}")

    if dekurs_datum != datum_vlozeni:
        print(f"    → jiný den ({dekurs_datum} ≠ {datum_vlozeni}), vytvoříme nový (INSERT)")
        return None

    # Krok 2: ověř přes vlákno s timeoutem zda záznam není zamčený
    print(f"    → dnešní den ({datum_vlozeni}) ✓ – ověřuji zámek (timeout {timeout_sec}s)...")
    vysledek = {}
    t = threading.Thread(target=_pokus_o_zamek, args=(dekurs_id, vysledek), daemon=True)
    t.start()
    t.join(timeout=timeout_sec)

    if t.is_alive():
        # Vlákno stále čeká na zámek = záznam drží Medicus
        raise RuntimeError(f"DEKURZ ID={dekurs_id} je zamčený (Medicus má záznam otevřený)")

    if 'chyba' in vysledek:
        raise fdb.DatabaseError(vysledek['chyba'])

    print(f"    → záznam volný, pokračuji se zápisem")
    return (dekurs_id, dekurs_rtf)


def ma_sekci_prilohy(rtf):
    return PRILOHY_HEADER in rtf


def pridat_do_sekce_prilohy(rtf, bookmark_list, filenameforbookmark_list):
    """Přidá více souborů do EXISTUJÍCÍ sekce 'Vložené přílohy'.

    Postup:
      1. Spočítá počet Files: odkazů = N → nové indexy začínají od N
      2. Vloží nové \\pard řádky před uzavírací prázdný řádek sekce
      3. Přidá nové bookmarky na konec {\\info{\\bookmarks ...}}
    """
    # 1. Počet existujících Files: odkazů
    bkm_match = re.search(r'\{\\info\{\\bookmarks ([^}]*)\}\}', rtf)
    if bkm_match:
        bkm_entries = [e for e in bkm_match.group(1).split(';') if e.strip()]
        n_files = sum(1 for e in bkm_entries if '"Files:' in e)
    else:
        n_files = 0
    print(f"    Počet existujících Files odkazů: {n_files}, přidávám {len(bookmark_list)} nových")

    # 2. Vložit nové \pard řádky před PRILOHY_CLOSING
    prilohy_pos = rtf.find(PRILOHY_HEADER)
    closing_pos = rtf.find(PRILOHY_CLOSING, prilohy_pos)
    if closing_pos == -1:
        raise RuntimeError("Nenalezen uzavírací řádek sekce Vložené přílohy!")

    new_pards = ''
    for i, fname in enumerate(filenameforbookmark_list):
        idx = n_files + i
        new_pards += (r'\pard\s10{\*\bkmkstart ' + str(idx) + r'}'
                      r'\plain\cs32\f0\ul\fs20\cf1 ' + fname
                      + r'{\*\bkmkend ' + str(idx) + r'}\par' + '\n')

    rtf = rtf[:closing_pos] + new_pards + rtf[closing_pos:]

    # 3. Přidat nové bookmarky na konec {\info{\bookmarks ...}}
    def append_bookmarks(m):
        entries = [e for e in m.group(1).split(';') if e.strip()]
        entries.extend(bookmark_list)
        return '{\\info{\\bookmarks ' + ';'.join(entries) + '}}'

    rtf = re.sub(r'\{\\info\{\\bookmarks ([^}]*)\}\}', append_bookmarks, rtf)
    return rtf


def merge_rtf_prepend(existing_rtf, new_bkm_list, new_body_pards, n_new):
    """Vloží novou sekci příloh na ZAČÁTEK stávajícího dekurzu (sekce tam ještě není)."""
    rtf = existing_rtf
    rtf = re.sub(r'\\bkmkstart (\d+)',
                 lambda m: '\\bkmkstart ' + str(int(m.group(1)) + n_new), rtf)
    rtf = re.sub(r'\\bkmkend (\d+)',
                 lambda m: '\\bkmkend '   + str(int(m.group(1)) + n_new), rtf)

    new_bkm_str = ';'.join(new_bkm_list)

    def merge_bkm(m):
        existing = m.group(1).strip()
        combined = new_bkm_str + (';' + existing if existing else '')
        return '{\\info{\\bookmarks ' + combined + '}}'

    if re.search(r'\{\\info\{\\bookmarks', rtf):
        rtf = re.sub(r'\{\\info\{\\bookmarks ([^}]*)\}\}', merge_bkm, rtf)
    else:
        rtf = re.sub(r'(\\deflang\d+)',
                     r'\1{\\info{\\bookmarks ' + new_bkm_str + '}}', rtf, count=1)

    match = re.search(r'\\uc1\\pard', rtf)
    if match:
        pos = match.start()
        rtf = rtf[:pos] + new_body_pards + '\n' + rtf[pos:]
    return rtf


# Šablona RTF pro nový dekurs
RTF_TEMPLATE = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks BOOKMARKNAMES}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs32\f0\ul\fs20\cf1 Odkaz;}}
BOOKMARKSTEXT
\pard\s10\plain\cs15\f0\fs20 \par
}"""

# ─── Hlavní tělo skriptu ──────────────────────────────────────────────────────

info = []
for soubor in os.listdir(cesta):
    if os.path.isfile(os.path.join(cesta, soubor)):
        print(soubor)
        if kontrola_struktury(soubor, conn):
            info.append(vrat_info_o_souboru(soubor, conn))
        else:
            prejmenuj_chybny_soubor(soubor, cesta)

info = sorted(info, key=lambda x: (x[0], x[1]))
print(info)

skupiny = {}
for row in info:
    skupiny[row[0]] = []
for row in info:
    skupiny[row[0]].append(row)

for key in skupiny.keys():
    print(f"\n{'='*60}")
    print(f"RC: {key}, souborů: {len(skupiny[key])}")

    idpac        = skupiny[key][0][1]
    datumzapisu  = datetime.datetime.now().date()
    caszapisu    = datetime.datetime.now().time()

    # ── PRE-CHECK: zkus zamknout dnešní dekurz PŘED zpracováním souborů ──────
    print(f"\n>>> Kontrola zámku dekurzu pro IDPAC={idpac}...")
    try:
        existujici = zkus_zamknout_dnesni_dekurs(conn, idpac, datumzapisu)
    except RuntimeError as e:
        # Vlákno nepřišlo do timeoutu = záznam drží Medicus
        print(f"\n!!! DEKURZ ZAMČEN – soubory skupiny RC={key} přeskočeny.")
        print("    Spusťte skript znovu až bude záznam volný.")
        continue
    except fdb.DatabaseError as e:
        chyba = str(e).lower()
        if 'deadlock' in chyba or 'lock conflict' in chyba or 'update conflict' in chyba:
            print(f"\n!!! DEKURZ ZAMČEN (DB konflikt) – soubory skupiny RC={key} přeskočeny.")
            print("    Spusťte skript znovu až bude záznam volný.")
            continue
        raise  # jiná DB chyba – propaguj dál

    cislo                    = 9
    poradi                   = 0
    bookmark_list            = []
    filenameforbookmark_list = []
    bookmarks_body           = ''

    # ── Krok 1: vložit každý soubor do ext DB + přesunout do zpracovaných ────
    for row in skupiny[key]:
        fileid = funkce_ext.zapis_file_ext(
            vstupconnection=conn, idpac=row[1],
            cesta=cesta, souborname=row[6], prvnizavorka=row[4],
            soubordate=row[2], souborfiledate=row[7], poznamka=row[5])
        print(f"    → FILES.ID = {fileid}  ({row[6]})")

        # Přesun souboru do zpracovaných
        for attempt in range(3):
            try:
                dest = os.path.join(cestazpracovana, row[6])
                if not os.path.exists(dest):
                    shutil.move(os.path.join(cesta, row[6]), dest)
                else:
                    ts = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S")
                    shutil.move(os.path.join(cesta, row[6]),
                                os.path.join(cestazpracovana, row[6][:-4] + " " + ts + ".pdf"))
                print("    Přesun OK!")
                break
            except Exception as e:
                print(f"    Attempt {attempt + 1} failed: {e}")
                if attempt < 2:
                    print("    Retrying in 5 seconds...")
                    time.sleep(5)
                else:
                    print("    Max retries reached. Command failed.")

        filenameforbookmark = row[2].strftime('%Y-%m-%d') + ' ' + row[4] + ': ' + row[5]
        bookmark_list.append('"' + filenameforbookmark + '","Files:' + str(fileid) + '",' + str(cislo))
        filenameforbookmark_list.append(filenameforbookmark)
        cislo += 7

        bookmarks_body += (r'\pard\s10{\*\bkmkstart ' + str(poradi) + r'}'
                           r'\plain\cs32\f0\ul\fs20\cf1 ' + filenameforbookmark
                           + r'{\*\bkmkend ' + str(poradi) + r'}\par')
        poradi += 1

    # ── Krok 2: sestavit tělo nové sekce příloh ───────────────────────────────
    new_body = (r'\uc1\pard\s10\plain\cs20\f0\i\fs20 Vlo\'9een\'e9 p\'f8\'edlohy:\par' + '\n'
                + bookmarks_body + '\n'
                + r'\pard\s10\plain\cs15\f0\fs20 \par')

    # ── Krok 3: rozhodovací logika (3 případy) ────────────────────────────────
    cur = conn.cursor()

    if existujici:
        dekurs_id, existing_rtf = existujici

        if ma_sekci_prilohy(existing_rtf):
            # Případ 1: dnešní dekurz má sekci příloh → přidáme soubory dovnitř
            print(f"\n>>> Sekce 'Vložené přílohy' nalezena v DEKURS ID={dekurs_id}")
            print(">>> Přidávám soubory DO existující sekce...")
            merged_rtf = pridat_do_sekce_prilohy(existing_rtf, bookmark_list, filenameforbookmark_list)
        else:
            # Případ 2: dnešní dekurz existuje, ale sekci příloh nemá → prepend
            print(f"\n>>> DEKURS ID={dekurs_id} nemá sekci příloh → vkládám sekci na začátek...")
            merged_rtf = merge_rtf_prepend(existing_rtf, bookmark_list, new_body, len(skupiny[key]))

        print("\n=== Výsledný RTF ===")
        print(merged_rtf)
        cur.execute("UPDATE DEKURS SET DEKURS = ? WHERE ID = ?", (merged_rtf, dekurs_id))
        conn.commit()
        print(f"\n>>> UPDATE DEKURS ID={dekurs_id} – hotovo!")

    else:
        # Případ 3: žádný dnešní dekurz → vytvoříme nový
        print(f"\n>>> Žádný dekurs pro dnešek → vytvářím nový...")
        bookmark_str = ';'.join(bookmark_list)
        rtf = RTF_TEMPLATE.replace('BOOKMARKNAMES', bookmark_str)
        rtf = rtf.replace('BOOKMARKSTEXT', new_body)

        print("\n=== Výsledný RTF ===")
        print(rtf)

        dekursid = funkce.get_dekurs_id(conn)
        cur.execute(
            "INSERT INTO DEKURS (id, iduzi, idprac, idodd, idpac, datum, cas, dekurs)"
            " VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
            (dekursid, 6, 2, 2, idpac, datumzapisu, caszapisu, rtf)
        )
        conn.commit()
        print(f"\n>>> Nový DEKURS ID={dekursid}")

print("\n=== HOTOVO ===")
conn.close()
