import sys
import json
from typing import TYPE_CHECKING

from aiohttp.client_exceptions import ClientError
from kivy import base, utils
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.factory import Factory
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.utils import platform

from electrum.gui.kivy.i18n import _

from electrum.base_crash_reporter import BaseCrashReporter, EarlyExceptionsQueue
from electrum.logging import Logger

if TYPE_CHECKING:
    from electrum.gui.kivy.main_window import ElectrumWindow


Builder.load_string('''
<CrashReporter@Popup>
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: crash_message
            text_size: root.width, None
            size: self.texture_size
            size_hint: None, None
        Label:
            id: request_help_message
            text_size: root.width*.95, None
            size: self.texture_size
            size_hint: None, None
        BoxLayout:
            size_hint: 1, 0.1
        Button:
            text: 'Show report contents'
            height: '48dp'
            size_hint: 1, None
            on_release: root.show_contents()
        BoxLayout:
            size_hint: 1, 0.1
        Label:
            id: describe_error_message
            text_size: root.width, None
            size: self.texture_size
            size_hint: None, None
        TextInput:
            id: user_message
            size_hint: 1, 0.3
        BoxLayout:
            size_hint: 1, 0.7
        BoxLayout:
            size_hint: 1, None
            height: '48dp'
            orientation: 'horizontal'
            Button:
                height: '48dp'
                text: 'Send'
                on_release: root.send_report()
            Button:
                text: 'Never'
                on_release: root.show_never()
            Button:
                text: 'Not now'
                on_release: root.dismiss()

<CrashReportDetails@Popup>
    BoxLayout:
        orientation: 'vertical'
        ScrollView:
            do_scroll_x: False
            Label:
                id: contents
                text_size: root.width*.9, None
                size: self.texture_size
                size_hint: None, None
        Button:
            text: 'Close'
            height: '48dp'
            size_hint: 1, None
            on_release: root.dismiss()
''')


class CrashReporter(BaseCrashReporter, Factory.Popup):
    issue_template = """[b]Traceback[/b]

[i]{traceback}[/i]


[b]Additional information[/b]
 * Electrum version: {app_version}
 * Operating system: {os}
 * Wallet type: {wallet_type}
 * Locale: {locale}
        """

    def __init__(self, main_window: 'ElectrumWindow', exctype, value, tb):
        BaseCrashReporter.__init__(self, exctype, value, tb)
        Factory.Popup.__init__(self)
        self.main_window = main_window
        self.title = BaseCrashReporter.CRASH_TITLE
        self.title_size = "24sp"
        self.ids.crash_message.text = BaseCrashReporter.CRASH_MESSAGE
        self.ids.request_help_message.text = BaseCrashReporter.REQUEST_HELP_MESSAGE
        self.ids.describe_error_message.text = BaseCrashReporter.DESCRIBE_ERROR_MESSAGE
        self.ids.user_message.hint_text = BaseCrashReporter.USER_COMMENT_PLACEHOLDER

    def show_contents(self):
        details = CrashReportDetails(self.get_report_string())
        details.open()

    def show_popup(self, title, content):
        popup = Factory.Popup(title=title,
                              content=Label(text=content, text_size=(Window.size[0] * 3/4, None)),
                              size_hint=(3/4, 3/4))
        popup.open()

    def send_report(self):
        try:
            loop = self.main_window.network.asyncio_loop
            proxy = self.main_window.network.proxy
            # FIXME network request in GUI thread...
            response = BaseCrashReporter.send_report(self, loop, proxy, timeout=10)
        except Exception as e:
            self.logger.warning(f"Error sending crash report. exc={e!r}")
            self.show_popup(_('Unable to send report'), _("Please check your network connection."))
        else:
            text = response.text
            if response.url:
                text += f" You can track further progress on GitHub."
            self.show_popup(_('Report sent'), text)
            if location := response.url:
                self.open_url(location)
        self.dismiss()

    def on_dismiss(self):
        self.main_window.on_wizard_aborted()

    def open_url(self, url):
        if platform != 'android':
            return
        from jnius import autoclass, cast
        String = autoclass("java.lang.String")
        url = String(url)
        PythonActivity = autoclass('org.kivy.android.PythonActivity')
        activity = PythonActivity.mActivity
        Intent = autoclass('android.content.Intent')
        Uri = autoclass('android.net.Uri')
        browserIntent = Intent()
        # This line crashes the app:
        # browserIntent.setAction(Intent.ACTION_VIEW)
        # Luckily we don't need it because the OS is smart enough to recognize the URL
        browserIntent.setData(Uri.parse(url))
        currentActivity = cast('android.app.Activity', activity)
        currentActivity.startActivity(browserIntent)

    def show_never(self):
        self.main_window.electrum_config.SHOW_CRASH_REPORTER = False
        self.dismiss()

    def get_user_description(self):
        return self.ids.user_message.text

    def get_wallet_type(self):
        return self.main_window.wallet.wallet_type


class CrashReportDetails(Factory.Popup):
    def __init__(self, text):
        Factory.Popup.__init__(self)
        self.title = "Report Details"
        self.ids.contents.text = text
        print(text)


class ExceptionHook(base.ExceptionHandler, Logger):
    def __init__(self, main_window: 'ElectrumWindow'):
        base.ExceptionHandler.__init__(self)
        Logger.__init__(self)
        self.main_window = main_window
        if not main_window.electrum_config.SHOW_CRASH_REPORTER:
            EarlyExceptionsQueue.set_hook_as_ready()  # flush already queued exceptions
            return
        # For exceptions in Kivy:
        base.ExceptionManager.add_handler(self)
        # For everything else:
        sys.excepthook = lambda exctype, value, tb: self.handle_exception(value)
        EarlyExceptionsQueue.set_hook_as_ready()

    def handle_exception(self, _inst):
        exc_info = sys.exc_info()
        self.logger.error('exception caught by crash reporter', exc_info=exc_info)
        # Check if this is an exception from within the exception handler:
        import traceback
        for item in traceback.extract_tb(exc_info[2]):
            if item.filename.endswith("crash_reporter.py"):
                return
        e = CrashReporter(self.main_window, *exc_info)
        # Open in main thread:
        Clock.schedule_once(lambda _: e.open(), 0)
        return base.ExceptionManager.PASS
