#!/usr/bin/python3
import gi, threading, json
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib
import urllib.request, urllib.error

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='HTTP Tester')
        self.set_default_size(700, 560)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        vbox.set_margin_top(8); vbox.set_margin_bottom(8)
        vbox.set_margin_start(8); vbox.set_margin_end(8)
        self.add(vbox)
        # URL row
        hb = Gtk.Box(spacing=6)
        self.method = Gtk.ComboBoxText()
        for m in ['GET','POST','PUT','DELETE','PATCH']: self.method.append_text(m)
        self.method.set_active(0)
        self.method.set_size_request(90,-1)
        hb.pack_start(self.method, False, False, 0)
        self.url = Gtk.Entry(); self.url.set_placeholder_text('https://...')
        self.url.set_text('https://httpbin.org/get')
        hb.pack_start(self.url, True, True, 0)
        btn = Gtk.Button(label='Send')
        btn.connect('clicked', self.send)
        hb.pack_start(btn, False, False, 0)
        vbox.pack_start(hb, False, False, 0)
        # Headers entry
        hb2 = Gtk.Box(spacing=6)
        hb2.add(Gtk.Label(label='Headers (Key: Value, one per line):', xalign=0))
        vbox.pack_start(hb2, False, False, 0)
        sw_h = Gtk.ScrolledWindow(); sw_h.set_size_request(-1, 60)
        self.headers_tv = Gtk.TextView()
        self.headers_tv.get_buffer().set_text('Content-Type: application/json')
        sw_h.add(self.headers_tv)
        vbox.pack_start(sw_h, False, False, 0)
        vbox.add(Gtk.Label(label='Request Body:', xalign=0))
        sw_b = Gtk.ScrolledWindow(); sw_b.set_size_request(-1, 80)
        self.body_tv = Gtk.TextView()
        sw_b.add(self.body_tv)
        vbox.pack_start(sw_b, False, False, 0)
        self.status_lbl = Gtk.Label(label='', xalign=0)
        vbox.pack_start(self.status_lbl, False, False, 0)
        vbox.add(Gtk.Label(label='Response:', xalign=0))
        sw_r = Gtk.ScrolledWindow()
        self.resp_tv = Gtk.TextView(); self.resp_tv.set_editable(False)
        from gi.repository import Pango
        self.resp_tv.override_font(Pango.FontDescription('Monospace 9'))
        sw_r.add(self.resp_tv)
        vbox.pack_start(sw_r, True, True, 0)
    def send(self, *_):
        self.status_lbl.set_text('Sending...')
        threading.Thread(target=self._do_request, daemon=True).start()
    def _do_request(self):
        try:
            url = self.url.get_text().strip()
            method = self.method.get_active_text()
            buf = self.body_tv.get_buffer()
            body = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False).encode()
            hbuf = self.headers_tv.get_buffer()
            htext = hbuf.get_text(hbuf.get_start_iter(), hbuf.get_end_iter(), False)
            headers = {}
            for line in htext.splitlines():
                if ':' in line:
                    k, v = line.split(':', 1)
                    headers[k.strip()] = v.strip()
            req = urllib.request.Request(url, data=body if body and method!='GET' else None,
                                          headers=headers, method=method)
            with urllib.request.urlopen(req, timeout=10) as resp:
                code = resp.getcode()
                resp_headers = dict(resp.getheaders())
                rbody = resp.read().decode('utf-8', errors='replace')
                try: rbody = json.dumps(json.loads(rbody), indent=2)
                except Exception: pass
                GLib.idle_add(self._show_result, code, resp_headers, rbody)
        except Exception as e:
            GLib.idle_add(self._show_result, 'Error', {}, str(e))
    def _show_result(self, code, headers, body):
        self.status_lbl.set_markup(f'<b>Status: {code}</b>')
        hdr_txt = '\n'.join(f'{k}: {v}' for k,v in headers.items())
        self.resp_tv.get_buffer().set_text(f'--- Headers ---\n{hdr_txt}\n\n--- Body ---\n{body}')

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