#!/usr/pkg/bin/python3.13
"""
Syncthing-GTK - IDDialog

Dialog with Device ID and generated QR code
"""


import http.client
import logging
import os
import ssl
import tempfile
import urllib.request

from gi.repository import Gio, GLib

from syncthing_gtk.uibuilder import UIBuilder

from .tools import IS_WINDOWS

log = logging.getLogger("IDDialog")


class IDDialog(object):
    """Dialog with Device ID and generated QR code"""

    def __init__(self, app, device_id):
        self.app = app
        self.device_id = device_id
        self.setup_widgets()
        self.ssl_ctx = create_ssl_context()
        self.load_data()

    def __getitem__(self, name):
        """Convince method that allows widgets to be accessed via self["widget"]"""
        return self.builder.get_object(name)

    def show(self, parent=None):
        if parent is not None:
            self["dialog"].set_transient_for(parent)
        self["dialog"].show_all()

    def close(self):
        self["dialog"].hide()
        self["dialog"].destroy()

    def setup_widgets(self):
        # Load glade file
        self.builder = UIBuilder()
        self.builder.add_from_file(os.path.join(self.app.gladepath, "device-id.glade"))
        self.builder.connect_signals(self)
        self["vID"].set_text(self.device_id)

    def load_data(self):
        """Loads QR code from Syncthing daemon"""
        if IS_WINDOWS:
            return self.load_data_urllib()
        uri = "%s/qr/?text=%s" % (self.app.daemon.get_webui_url(), self.device_id)
        io = Gio.file_new_for_uri(uri)
        io.load_contents_async(None, self.cb_syncthing_qr, ())

    def load_data_urllib(self):
        """Loads QR code from Syncthing daemon"""
        uri = "%s/qr/?text=%s" % (self.app.daemon.get_webui_url(), self.device_id)
        api_key = self.app.daemon.get_api_key()
        opener = urllib.request.build_opener(DummyHTTPSHandler(self.ssl_ctx))
        if api_key is not None:
            opener.addheaders = [("X-API-Key", api_key)]
        a = opener.open(uri)
        data = a.read()
        tf = tempfile.NamedTemporaryFile("wb", suffix=".png", delete=False)
        tf.write(data)
        tf.close()
        self["vQR"].set_from_file(tf.name)
        os.unlink(tf.name)

    def cb_btClose_clicked(self, *a):
        self.close()

    def cb_syncthing_qr(self, io, results, *a):
        """
        Called when QR code is loaded or operation fails. Image is then
        displayed in dialog, failure is silently ignored.
        """
        try:
            ok, contents, etag = io.load_contents_finish(results)
            if ok:
                # QR is loaded, save it to temp file and let GTK to handle
                # rest
                tf = tempfile.NamedTemporaryFile("wb", suffix=".png", delete=False)
                tf.write(contents)
                tf.close()
                self["vQR"].set_from_file(tf.name)
                os.unlink(tf.name)
        except GLib.Error as e:
            if e.code in [14, 15]:
                # Unauthorized. Grab CSRF token from daemon and try again
                log.warning("Failed to load image using glib. Retrying with urllib2.")
                self.load_data_urllib()
        except Exception as e:
            log.exception(e)
            return
        finally:
            del io


def create_ssl_context():
    """May return NULL if ssl is not available"""
    if hasattr(ssl, "create_default_context"):
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
    else:
        log.warning("SSL is not available, cannot verify server certificate.")


class DummyHTTPSHandler(urllib.request.HTTPSHandler):
    """
    Dummy HTTPS handler that ignores certificate errors. This in unsafe,
    but used ONLY for QR code images.
    """

    def __init__(self, ctx):
        urllib.request.HTTPSHandler.__init__(self)
        self.ctx = ctx

    def https_open(self, req):
        return self.do_open(self.getConnection, req)

    def getConnection(self, host, timeout=300):
        if self.ctx is not None:
            return http.client.HTTPSConnection(host, context=self.ctx)
        return True
