#!/usr/bin/python3
import gi, base64, json
from datetime import datetime
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Pango

def b64_decode(s):
    s = s.replace('-', '+').replace('_', '/')
    pad = 4 - len(s) % 4
    if pad != 4: s += '=' * pad
    return base64.b64decode(s).decode('utf-8', errors='replace')

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='JWT Decoder')
        self.set_default_size(600, 500)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        vbox.set_margin_top(10); vbox.set_margin_bottom(10)
        vbox.set_margin_start(10); vbox.set_margin_end(10)
        self.add(vbox)
        vbox.add(Gtk.Label(label='Paste JWT token:', xalign=0))
        self.inp = Gtk.Entry()
        self.inp.set_placeholder_text('eyJ...')
        vbox.pack_start(self.inp, False, False, 0)
        btn = Gtk.Button(label='Decode')
        btn.connect('clicked', self.decode)
        self.inp.connect('activate', self.decode)
        vbox.pack_start(btn, False, False, 0)
        nb = Gtk.Notebook()
        for title in ['Header', 'Payload', 'Info']:
            sw = Gtk.ScrolledWindow()
            tv = Gtk.TextView(); tv.set_editable(False)
            tv.override_font(Pango.FontDescription('Monospace 10'))
            tv.set_wrap_mode(Gtk.WrapMode.WORD)
            sw.add(tv)
            nb.append_page(sw, Gtk.Label(label=title))
        self.tvs = [nb.get_nth_page(i).get_child() for i in range(3)]
        vbox.pack_start(nb, True, True, 0)
    def decode(self, *_):
        token = self.inp.get_text().strip()
        parts = token.split('.')
        if len(parts) < 2:
            self.tvs[0].get_buffer().set_text('Invalid JWT')
            return
        try:
            header = json.loads(b64_decode(parts[0]))
            self.tvs[0].get_buffer().set_text(json.dumps(header, indent=2))
        except Exception as e:
            self.tvs[0].get_buffer().set_text(f'Error: {e}')
        try:
            payload = json.loads(b64_decode(parts[1]))
            self.tvs[1].get_buffer().set_text(json.dumps(payload, indent=2))
            info = []
            if 'exp' in payload:
                exp = datetime.fromtimestamp(payload['exp'])
                now = datetime.now()
                status = 'EXPIRED' if exp < now else 'valid'
                info.append(f'Expires: {exp.strftime("%Y-%m-%d %H:%M:%S")} ({status})')
            if 'iat' in payload:
                iat = datetime.fromtimestamp(payload['iat'])
                info.append(f'Issued:  {iat.strftime("%Y-%m-%d %H:%M:%S")}')
            if 'sub' in payload: info.append(f'Subject: {payload["sub"]}')
            if 'iss' in payload: info.append(f'Issuer:  {payload["iss"]}')
            algo = header.get('alg', 'unknown') if isinstance(header, dict) else 'unknown'
            info.append(f'Algorithm: {algo}')
            info.append(f'Signature: {parts[2][:20]}... (not verified)')
            self.tvs[2].get_buffer().set_text('\n'.join(info))
        except Exception as e:
            self.tvs[1].get_buffer().set_text(f'Error: {e}')

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
