import sys
import os
import re
import json
import time
import ctypes
import urllib.parse
import urllib.request
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QLabel, QPushButton, QLineEdit, QFrame, QMessageBox,
    QFileDialog, QGraphicsDropShadowEffect, QScrollArea, QSizePolicy
)
from PyQt6.QtCore import QProcess, Qt, QTimer, pyqtSignal, pyqtProperty, QPropertyAnimation, QEasingCurve, QThread
from PyQt6.QtGui import QFont, QColor, QCursor, QPainter, QBrush, QPen, QPainterPath, QIcon, QPixmap, QScreen
from PyQt6.QtCore import QPointF

# -----------------------------------------------------------
# הגדרות מערכת ונתיבים
# -----------------------------------------------------------
SCRIPT_DIR = os.path.dirname(os.path.abspath(sys.argv[0]))
SETTINGS_FILE = os.path.join(SCRIPT_DIR, "control_panel_config.json")
NETFREE_CERT = r"C:\ProgramData\NetFree\CA\netfree-ca-bundle-curl.crt"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
STARTUP_DIR = os.path.join(os.environ.get("APPDATA", ""), r"Microsoft\Windows\Start Menu\Programs\Startup")
BAT_NAME = "SmartHomeControlPanel.bat"
BAT_PATH = os.path.join(STARTUP_DIR, BAT_NAME)

# -----------------------------------------------------------
# מנוע עבודת רקע - למניעת תקיעות בממשק (Multithreading)
# -----------------------------------------------------------
class WorkerThread(QThread):
    finished = pyqtSignal(object)
    error = pyqtSignal(str)

    def __init__(self, func, *args, **kwargs):
        super().__init__()
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def run(self):
        try:
            result = self.func(*self.args, **self.kwargs)
            self.finished.emit(result)
        except Exception as e:
            self.error.emit(str(e))

# -----------------------------------------------------------
# מנוע UI: אייקונים וקטוריים ורכיבים מעוצבים
# -----------------------------------------------------------
class IconFactory:
    @staticmethod
    def create(name, color="#4F46E5", size=20):
        scale = 4 
        pixmap = QPixmap(size * scale, size * scale)
        pixmap.fill(Qt.GlobalColor.transparent)
        
        p = QPainter(pixmap)
        p.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform)
        p.scale(scale, scale)
        
        pen = QPen(QColor(color))
        pen.setWidthF(2.0)
        pen.setCapStyle(Qt.PenCapStyle.RoundCap)
        pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
        p.setPen(pen)
        p.setBrush(Qt.BrushStyle.NoBrush)
        
        ratio = size / 24.0
        p.scale(ratio, ratio)
        
        if name == "power":
            p.drawArc(3, 3, 18, 18, 120 * 16, 300 * 16)
            p.drawLine(12, 2, 12, 11)
        elif name == "play":
            path = QPainterPath()
            path.moveTo(8, 5)
            path.lineTo(19, 12)
            path.lineTo(8, 19)
            path.closeSubpath()
            p.drawPath(path)
        elif name == "stop":
            p.drawRoundedRect(6, 6, 12, 12, 2, 2)
        elif name == "globe":
            p.drawEllipse(3, 3, 18, 18)
            p.drawEllipse(7, 3, 10, 18)
            p.drawLine(3, 12, 21, 12)
        elif name == "sync":
            p.drawArc(3, 3, 18, 18, 45 * 16, 135 * 16)
            p.drawLine(3, 12, 3, 7)
            p.drawLine(3, 12, 8, 12)
            p.drawArc(3, 3, 18, 18, 225 * 16, 135 * 16)
            p.drawLine(21, 12, 21, 17)
            p.drawLine(21, 12, 16, 12)
        elif name == "folder":
            path = QPainterPath()
            path.moveTo(3, 7)
            path.arcTo(3, 3, 4, 4, 180, -90)
            path.lineTo(9, 3)
            path.lineTo(12, 6)
            path.lineTo(19, 6)
            path.arcTo(17, 6, 4, 4, 90, -90)
            path.lineTo(21, 19)
            path.arcTo(17, 17, 4, 4, 0, -90)
            path.lineTo(5, 21)
            path.arcTo(3, 17, 4, 4, 270, -90)
            path.closeSubpath()
            p.drawPath(path)
        elif name == "search":
            p.drawEllipse(4, 4, 12, 12)
            p.drawLine(QPointF(14.24, 14.24), QPointF(21.0, 21.0)) 
        elif name == "check":
            p.drawLine(20, 7, 10, 17)
            p.drawLine(10, 17, 5, 12)
        p.end()
        pixmap.setDevicePixelRatio(scale)
        return QIcon(pixmap)


class ToggleSwitch(QWidget):
    toggled = pyqtSignal(bool)

    def __init__(self, checked=False, parent=None):
        super().__init__(parent)
        self.setFixedSize(46, 24)
        self.setCursor(Qt.CursorShape.PointingHandCursor)
        self._checked = checked
        self._position = 24.0 if checked else 2.0
        
        self.anim = QPropertyAnimation(self, b"position")
        self.anim.setEasingCurve(QEasingCurve.Type.OutCirc)
        self.anim.setDuration(250)
        
    @pyqtProperty(float)
    def position(self): return self._position
        
    @position.setter
    def position(self, pos):
        self._position = pos
        self.update()
        
    def setChecked(self, checked):
        if self._checked != checked:
            self._checked = checked
            self.anim.setEndValue(24.0 if checked else 2.0)
            self.anim.start()
            self.toggled.emit(checked)
            
    def isChecked(self): return self._checked
        
    def mouseReleaseEvent(self, event):
        self.setChecked(not self._checked)
        super().mouseReleaseEvent(event)
        
    def paintEvent(self, event):
        p = QPainter(self)
        p.setRenderHint(QPainter.RenderHint.Antialiasing)
        
        bg_color = QColor("#10B981") if self._checked else QColor("#CBD5E1")
        p.setBrush(QBrush(bg_color))
        p.setPen(Qt.PenStyle.NoPen)
        p.drawRoundedRect(0, 0, self.width(), self.height(), 12, 12)
        
        shadow_pos = int(self._position)
        p.setBrush(QColor(0, 0, 0, 20))
        p.drawEllipse(shadow_pos, 3, 20, 20)
        p.setBrush(QColor(0, 0, 0, 10))
        p.drawEllipse(shadow_pos, 4, 20, 20)
        
        p.setBrush(QBrush(QColor("#FFFFFF")))
        p.drawEllipse(shadow_pos, 2, 20, 20)


class StatusBadge(QLabel):
    def __init__(self):
        super().__init__()
        self.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setFixedHeight(24)
        self.current_state = "offline"
        self.is_blink_on = True
        
        self.blink_timer = QTimer(self)
        self.blink_timer.timeout.connect(self.toggle_blink)
        self.set_state("offline")

    def toggle_blink(self):
        self.is_blink_on = not self.is_blink_on
        self._update_ui()

    def _update_ui(self):
        dot = "●"
        if self.current_state == "online":
            dot_color = "#10B981" if self.is_blink_on else "#A7F3D0"
            self.setText(f"<span style='color:{dot_color};'>{dot}</span> פעיל")
            self.setStyleSheet("background-color: #ECFDF5; border: 1px solid #A7F3D0; color: #065F46; border-radius: 12px; padding: 0 10px; font-weight: 700; font-size: 11.5px;")
        elif self.current_state == "offline":
            self.setText(f"<span style='color:#94A3B8;'>{dot}</span> מנותק")
            self.setStyleSheet("background-color: #F8FAFC; border: 1px solid #E2E8F0; color: #475569; border-radius: 12px; padding: 0 10px; font-weight: 700; font-size: 11.5px;")
        elif self.current_state == "warning":
            self.setText(f"<span style='color:#F59E0B;'>{dot}</span> ממתין")
            self.setStyleSheet("background-color: #FFFBEB; border: 1px solid #FDE68A; color: #92400E; border-radius: 12px; padding: 0 10px; font-weight: 700; font-size: 11.5px;")

    def set_state(self, state):
        self.current_state = state
        self.is_blink_on = True
        if state == "online":
            self.blink_timer.start(700)
        else:
            self.blink_timer.stop()
        self._update_ui()


def apply_shadow(widget):
    shadow = QGraphicsDropShadowEffect()
    shadow.setBlurRadius(20)
    shadow.setColor(QColor(15, 23, 42, 10))
    shadow.setOffset(0, 4)
    widget.setGraphicsEffect(shadow)

def create_divider():
    div = QWidget()
    div.setFixedHeight(1)
    div.setStyleSheet("background-color: #E2E8F0;")
    return div


# -----------------------------------------------------------
# חלון המערכת הראשי
# -----------------------------------------------------------
class SmartHomeControlPanel(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("ניהול שרת חכם - Home Assistant")
        self.setLayoutDirection(Qt.LayoutDirection.RightToLeft)
        
        screen_geometry = QApplication.primaryScreen().availableGeometry()
        window_width = 850
        window_height = min(900, screen_geometry.height() - 120)
        self.resize(window_width, window_height) 

        self.ha_process = None
        self.cf_process = None
        self.current_url = ""
        self.cf_output_buffer = "" # לוכד טקסט חלקי שמגיע מ-CF כדי לא לפספס את הלינק

        self.init_ui()
        self.apply_styles()
        self.load_settings()
        self.update_ui_buttons()

        self.last_keepalive = time.time()
        self.sleep_monitor_timer = QTimer(self)
        self.sleep_monitor_timer.timeout.connect(self.monitor_sleep_wakeup)
        self.sleep_monitor_timer.start(5000)

        # משתנים לשמירת תהליכוני רקע (למניעת קריסה ב-Garbage Collection)
        self.threads = []

        if "--autostart" in sys.argv:
            QTimer.singleShot(1500, self.start_all)

    def init_ui(self):
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setFrameShape(QFrame.Shape.NoFrame)
        self.setCentralWidget(scroll_area)

        main_widget = QWidget()
        scroll_area.setWidget(main_widget)
        
        outer_layout = QVBoxLayout(main_widget)
        outer_layout.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
        
        container = QWidget()
        container.setMaximumWidth(820)
        layout = QVBoxLayout(container)
        layout.setContentsMargins(0, 45, 0, 50)
        layout.setSpacing(30)
        outer_layout.addWidget(container)

        # כותרת עליונה
        header_layout = QHBoxLayout()
        title_box = QVBoxLayout()
        title_box.setSpacing(4)
        title_box.addWidget(QLabel("דשבורד מערכת", objectName="main_title"))
        title_box.addWidget(QLabel("ניהול שרת מקומי, ענן וסנכרון חכם", objectName="subtitle"))
        header_layout.addLayout(title_box)
        
        header_layout.addStretch()
        
        self.btn_master = QPushButton(" הפעלה כוללת")
        self.btn_master.setIcon(IconFactory.create("power", "#FFFFFF", 20))
        self.btn_master.setObjectName("btn_master_start")
        self.btn_master.setFixedHeight(44)
        self.btn_master.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.btn_master.clicked.connect(self.toggle_master)
        header_layout.addWidget(self.btn_master)
        
        layout.addLayout(header_layout)

        # 1. תפעול שירותים
        layout.addWidget(QLabel("סטטוס מערכות ותפעול", objectName="section_title"))

        services_card = QFrame(objectName="card")
        apply_shadow(services_card)
        srv_layout = QVBoxLayout(services_card)
        srv_layout.setContentsMargins(0, 0, 0, 0)
        srv_layout.setSpacing(0)

        # שרת HA
        ha_row = QWidget()
        ha_layout = QHBoxLayout(ha_row)
        ha_layout.setContentsMargins(30, 20, 30, 20)
        
        ha_text_layout = QVBoxLayout()
        ha_text_layout.setSpacing(4)
        
        ha_title_row = QHBoxLayout()
        ha_title_row.addWidget(QLabel("שרת Home Assistant", objectName="row_title"))
        self.badge_ha = StatusBadge()
        ha_title_row.addWidget(self.badge_ha)
        ha_title_row.addStretch()
        
        ha_text_layout.addLayout(ha_title_row)
        ha_text_layout.addWidget(QLabel("מנוע הבית החכם המקומי", objectName="row_desc"))
        
        ha_layout.addLayout(ha_text_layout)
        ha_layout.addStretch()
        
        btn_browser = QPushButton(" פתח בדפדפן")
        btn_browser.setIcon(IconFactory.create("globe", "#4338CA", 16))
        btn_browser.setObjectName("btn_outline")
        btn_browser.setFixedHeight(40)
        btn_browser.clicked.connect(lambda: os.system("start http://127.0.0.1:8123"))
        
        self.btn_toggle_ha = QPushButton(" הפעלה")
        self.btn_toggle_ha.setIcon(IconFactory.create("play", "#FFFFFF", 16))
        self.btn_toggle_ha.setObjectName("btn_primary")
        self.btn_toggle_ha.setFixedHeight(40)
        self.btn_toggle_ha.clicked.connect(self.toggle_ha)
        
        ha_btn_layout = QHBoxLayout()
        ha_btn_layout.setSpacing(12)
        ha_btn_layout.addWidget(btn_browser)
        ha_btn_layout.addWidget(self.btn_toggle_ha)
        ha_layout.addLayout(ha_btn_layout)
        
        srv_layout.addWidget(ha_row)
        srv_layout.addWidget(create_divider())

        # מנהרת ענן
        cf_row = QWidget()
        cf_layout = QHBoxLayout(cf_row)
        cf_layout.setContentsMargins(30, 20, 30, 20)
        
        cf_text_layout = QVBoxLayout()
        cf_text_layout.setSpacing(4)
        
        cf_title_row = QHBoxLayout()
        cf_title_row.addWidget(QLabel("מנהרת Cloudflare", objectName="row_title"))
        self.badge_cf = StatusBadge()
        cf_title_row.addWidget(self.badge_cf)
        cf_title_row.addStretch()
        
        cf_text_layout.addLayout(cf_title_row)
        cf_text_layout.addWidget(QLabel("קישור מאובטח וגישה מרחוק", objectName="row_desc"))
        
        cf_layout.addLayout(cf_text_layout)
        cf_layout.addStretch()
        
        self.btn_force_sync = QPushButton(" סנכרון API")
        self.btn_force_sync.setIcon(IconFactory.create("sync", "#4338CA", 16))
        self.btn_force_sync.setObjectName("btn_outline")
        self.btn_force_sync.setFixedHeight(40)
        self.btn_force_sync.clicked.connect(self.force_update_yemot)

        self.btn_toggle_cf = QPushButton(" חיבור לענן")
        self.btn_toggle_cf.setIcon(IconFactory.create("play", "#FFFFFF", 16))
        self.btn_toggle_cf.setObjectName("btn_primary")
        self.btn_toggle_cf.setFixedHeight(40)
        self.btn_toggle_cf.clicked.connect(self.toggle_cf)
        
        cf_btn_layout = QHBoxLayout()
        cf_btn_layout.setSpacing(12)
        cf_btn_layout.addWidget(self.btn_force_sync)
        cf_btn_layout.addWidget(self.btn_toggle_cf)
        cf_layout.addLayout(cf_btn_layout)
        
        srv_layout.addWidget(cf_row)
        srv_layout.addWidget(create_divider())
        
        # שדה URL
        url_container = QWidget()
        url_layout = QVBoxLayout(url_container)
        url_layout.setContentsMargins(30, 20, 30, 20)
        self.url_display = QLabel("המתן לחיבור כדי לקבל כתובת גישה חיצונית...")
        self.url_display.setObjectName("url_display")
        self.url_display.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
        url_layout.addWidget(self.url_display)
        srv_layout.addWidget(url_container)

        layout.addWidget(services_card)

        # 2. הגדרות תצורה
        layout.addSpacing(5)
        layout.addWidget(QLabel("הגדרות מתקדמות", objectName="section_title"))

        settings_card = QFrame(objectName="card")
        apply_shadow(settings_card)
        set_layout = QVBoxLayout(settings_card)
        set_layout.setContentsMargins(0, 0, 0, 0)
        set_layout.setSpacing(0)

        LABEL_WIDTH = 170 

        # שורה 1
        row1 = QWidget()
        lyt1 = QHBoxLayout(row1)
        lyt1.setContentsMargins(30, 20, 30, 20)
        lyt1.setSpacing(15)
        lbl1 = QLabel("תיקיית מערכת", objectName="setting_label")
        lbl1.setFixedWidth(LABEL_WIDTH) 
        self.hass_path_input = QLineEdit()
        self.hass_path_input.setFixedHeight(40)
        self.hass_path_input.setPlaceholderText("לדוגמה: C:\\HassWP")
        btn_browse = QPushButton(" עיון בתיקיות...")
        btn_browse.setIcon(IconFactory.create("folder", "#4338CA", 16))
        btn_browse.setObjectName("btn_outline")
        btn_browse.setFixedSize(145, 40)
        btn_browse.clicked.connect(self.browse_hass_folder)
        lyt1.addWidget(lbl1)
        lyt1.addWidget(self.hass_path_input)
        lyt1.addWidget(btn_browse)
        set_layout.addWidget(row1)
        
        set_layout.addWidget(create_divider())

        # שורה 2 
        row2 = QWidget()
        lyt2 = QHBoxLayout(row2)
        lyt2.setContentsMargins(30, 20, 30, 20)
        lyt2.setSpacing(15)
        lbl2 = QLabel("פרטי ימות המשיח", objectName="setting_label")
        lbl2.setFixedWidth(LABEL_WIDTH)
        
        self.yemot_num_input = QLineEdit()
        self.yemot_num_input.setFixedHeight(40)
        self.yemot_num_input.setPlaceholderText("מספר מערכת")
        
        self.yemot_pass_input = QLineEdit()
        self.yemot_pass_input.setFixedHeight(40)
        self.yemot_pass_input.setPlaceholderText("סיסמת ניהול")
        self.yemot_pass_input.setEchoMode(QLineEdit.EchoMode.Password)
        
        lyt2.addWidget(lbl2)
        lyt2.addWidget(self.yemot_num_input)
        lyt2.addWidget(self.yemot_pass_input)
        set_layout.addWidget(row2)

        set_layout.addWidget(create_divider())

        # שורה 3 
        row3 = QWidget()
        lyt3 = QHBoxLayout(row3)
        lyt3.setContentsMargins(30, 20, 30, 20)
        lyt3.setSpacing(15)
        lbl3 = QLabel("שלוחות להתממשקות", objectName="setting_label")
        lbl3.setFixedWidth(LABEL_WIDTH) 
        self.extensions_input = QLineEdit()
        self.extensions_input.setFixedHeight(40)
        self.extensions_input.setPlaceholderText("לדוגמה: 1, 9, 2/1")
        self.btn_auto_scan = QPushButton(" איתור אוטומטי")
        self.btn_auto_scan.setIcon(IconFactory.create("search", "#4338CA", 16))
        self.btn_auto_scan.setObjectName("btn_outline")
        self.btn_auto_scan.setFixedSize(145, 40)
        self.btn_auto_scan.clicked.connect(self.auto_scan_extensions_ui)
        lyt3.addWidget(lbl3)
        lyt3.addWidget(self.extensions_input)
        lyt3.addWidget(self.btn_auto_scan)
        set_layout.addWidget(row3)

        layout.addWidget(settings_card)

        # 3. אוטומציה והתנהגות
        layout.addSpacing(5)
        layout.addWidget(QLabel("אוטומציה ותחזוקה", objectName="section_title"))
        
        beh_card = QFrame(objectName="card")
        apply_shadow(beh_card)
        beh_layout = QVBoxLayout(beh_card)
        beh_layout.setContentsMargins(0, 0, 0, 0)
        beh_layout.setSpacing(0)

        def create_toggle_row(title, desc, toggle_widget):
            row = QWidget()
            lyt = QHBoxLayout(row)
            lyt.setContentsMargins(30, 18, 30, 18)
            txt = QVBoxLayout()
            txt.setSpacing(2)
            txt.addWidget(QLabel(title, objectName="row_title"))
            txt.addWidget(QLabel(desc, objectName="row_desc"))
            lyt.addLayout(txt)
            lyt.addStretch()
            lyt.addWidget(toggle_widget, alignment=Qt.AlignmentFlag.AlignVCenter)
            return row

        self.chk_startup = ToggleSwitch(self.is_startup_enabled())
        self.chk_startup.toggled.connect(self.toggle_startup)
        beh_layout.addWidget(create_toggle_row("הפעל אוטומטית בעליית המחשב", "התוכנה והשרתים יופעלו מיד עם הדלקת המחשב", self.chk_startup))
        beh_layout.addWidget(create_divider())

        self.chk_prevent_sleep = ToggleSwitch(True)
        self.chk_prevent_sleep.toggled.connect(self.update_sleep_block_state)
        beh_layout.addWidget(create_toggle_row("מנע מהמחשב להיכנס למצב שינה", "מבטיח שהאוטומציות והחיבור מרחוק יעבדו ברציפות", self.chk_prevent_sleep))
        beh_layout.addWidget(create_divider())

        self.chk_wakeup_detect = ToggleSwitch(True)
        beh_layout.addWidget(create_toggle_row("רענון אוטומטי בחזרה משינה", "מחדש את התקשורת מול הענן במקרה של ניתוק ברשת", self.chk_wakeup_detect))

        layout.addWidget(beh_card)

        # כפתור שמירה
        save_layout = QHBoxLayout()
        btn_save = QPushButton(" שמור הגדרות")
        btn_save.setIcon(IconFactory.create("check", "#FFFFFF", 18))
        btn_save.setObjectName("btn_save")
        btn_save.setFixedHeight(44)
        btn_save.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        btn_save.clicked.connect(self.save_settings)
        save_layout.addStretch()
        save_layout.addWidget(btn_save)
        
        layout.addSpacing(15)
        layout.addLayout(save_layout)
        layout.addStretch()

    # =========================================================================
    # לוגיקה וניהול תהליכים (Process Lifecycle)
    # =========================================================================
    def update_ui_buttons(self):
        ha_running = self.ha_process and self.ha_process.state() == QProcess.ProcessState.Running
        cf_running = self.cf_process and self.cf_process.state() == QProcess.ProcessState.Running

        if ha_running:
            self.btn_toggle_ha.setText(" כיבוי שרת")
            self.btn_toggle_ha.setIcon(IconFactory.create("stop", "#FFFFFF", 16))
            self.btn_toggle_ha.setObjectName("btn_danger")
            self.badge_ha.set_state("online")
        else:
            self.btn_toggle_ha.setText(" הפעלה")
            self.btn_toggle_ha.setIcon(IconFactory.create("play", "#FFFFFF", 16))
            self.btn_toggle_ha.setObjectName("btn_primary")
            self.badge_ha.set_state("offline")

        if cf_running:
            self.btn_toggle_cf.setText(" ניתוק ענן")
            self.btn_toggle_cf.setIcon(IconFactory.create("stop", "#FFFFFF", 16))
            self.btn_toggle_cf.setObjectName("btn_danger")
            if "ממתין" not in self.badge_cf.text():
                self.badge_cf.set_state("online")
        else:
            self.btn_toggle_cf.setText(" חיבור לענן")
            self.btn_toggle_cf.setIcon(IconFactory.create("play", "#FFFFFF", 16))
            self.btn_toggle_cf.setObjectName("btn_primary")
            self.badge_cf.set_state("offline")
            self.url_display.setText("המתן לחיבור כדי לקבל כתובת גישה חיצונית...")

        if ha_running or cf_running:
            self.btn_master.setText(" סגירת מערכות מלאה")
            self.btn_master.setIcon(IconFactory.create("power", "#FFFFFF", 20))
            self.btn_master.setObjectName("btn_master_stop")
        else:
            self.btn_master.setText(" הפעלה כוללת")
            self.btn_master.setIcon(IconFactory.create("power", "#FFFFFF", 20))
            self.btn_master.setObjectName("btn_master_start")

        for widget in [self.btn_toggle_ha, self.btn_toggle_cf, self.btn_master]:
            widget.style().unpolish(widget)
            widget.style().polish(widget)

    def toggle_ha(self):
        if self.ha_process and self.ha_process.state() != QProcess.ProcessState.NotRunning:
            self.stop_ha()
        else:
            self.start_ha()

    def toggle_cf(self):
        if self.cf_process and self.cf_process.state() != QProcess.ProcessState.NotRunning:
            self.stop_cf()
        else:
            self.start_cf()

    def toggle_master(self):
        if (self.ha_process and self.ha_process.state() != QProcess.ProcessState.NotRunning) or \
           (self.cf_process and self.cf_process.state() != QProcess.ProcessState.NotRunning):
            self.stop_all()
        else:
            self.start_all()

    def prevent_sleep(self):
        try: ctypes.windll.kernel32.SetThreadExecutionState(0x80000000 | 0x00000001)
        except: pass

    def allow_sleep(self):
        try: ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
        except: pass

    def update_sleep_block_state(self):
        ha_running = self.ha_process and self.ha_process.state() != QProcess.ProcessState.NotRunning
        cf_running = self.cf_process and self.cf_process.state() != QProcess.ProcessState.NotRunning
        if (ha_running or cf_running) and self.chk_prevent_sleep.isChecked():
            self.prevent_sleep()
        else:
            self.allow_sleep()

    def monitor_sleep_wakeup(self):
        now = time.time()
        drift = now - self.last_keepalive
        self.last_keepalive = now
        if not self.chk_wakeup_detect.isChecked(): return
        if drift > 15 and self.cf_process and self.cf_process.state() != QProcess.ProcessState.NotRunning:
            self.badge_cf.set_state("warning")
            self.stop_cf()
            QTimer.singleShot(5000, self.start_cf)

    def browse_hass_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "בחירת נתיב שרת")
        if folder: self.hass_path_input.setText(os.path.normpath(folder))

    def load_settings(self):
        if os.path.exists(SETTINGS_FILE):
            try:
                with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.hass_path_input.setText(data.get("hass_wp_dir", ""))
                    self.yemot_num_input.setText(data.get("system_number", ""))
                    self.yemot_pass_input.setText(data.get("password", ""))
                    self.extensions_input.setText(data.get("extensions", "1, 9, 2/1"))
                    self.chk_prevent_sleep.setChecked(data.get("prevent_sleep", True))
                    self.chk_wakeup_detect.setChecked(data.get("wakeup_detect", True))
            except: pass
        else:
            self.extensions_input.setText("1, 9, 2/1")
            self.chk_prevent_sleep.setChecked(True)
            self.chk_wakeup_detect.setChecked(True)

    def save_settings(self):
        data = {
            "hass_wp_dir": self.hass_path_input.text().strip(),
            "system_number": self.yemot_num_input.text().strip(),
            "password": self.yemot_pass_input.text().strip(),
            "extensions": self.extensions_input.text().strip(),
            "prevent_sleep": self.chk_prevent_sleep.isChecked(),
            "wakeup_detect": self.chk_wakeup_detect.isChecked()
        }
        try:
            with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=4)
            QMessageBox.information(self, "מערכת", "התצורה נשמרה בהצלחה.")
        except Exception as e:
            QMessageBox.critical(self, "שגיאה", f"כשל בשמירה: {e}")

    # =========================================================================
    # איתור אוטומטי בענן - ברקע (QThread)
    # =========================================================================
    def auto_scan_extensions_ui(self):
        sys_num = self.yemot_num_input.text().strip()
        password = self.yemot_pass_input.text().strip()
        if not sys_num or not password:
            QMessageBox.warning(self, "שגיאה", "אנא הזן נתוני התחברות ל-API.")
            return

        token = f"{sys_num}:{password}"
        self.btn_auto_scan.setText(" סורק...")
        self.btn_auto_scan.setEnabled(False)
        
        # הרצה ב-Thread נפרד כדי למנוע קפיאת ממשק
        worker = WorkerThread(self._scan_yemot_folder_wrapper, token)
        worker.finished.connect(self.on_scan_finished)
        worker.error.connect(self.on_scan_error)
        self.threads.append(worker)
        worker.start()

    def _scan_yemot_folder_wrapper(self, token):
        # עטיפה כדי להחזיר תוצאה ל-Signal
        return self._scan_yemot_folder(token, "/", {"f": 0})

    def on_scan_finished(self, api_exts):
        self.extensions_input.clear()
        if api_exts:
            unique = list(dict.fromkeys(api_exts)) 
            self.extensions_input.setText(", ".join(unique))
            self.btn_auto_scan.setText(" אותר בהצלחה!")
            QMessageBox.information(self, "סריקה", f"נמצאו {len(unique)} שלוחות מוגדרות API.")
        else:
            self.btn_auto_scan.setText(" לא אותרו שלוחות")
            QMessageBox.warning(self, "סריקה", "לא נמצאו שלוחות המוגדרות כ-API.")
        
        QTimer.singleShot(2500, lambda: self._reset_btn(self.btn_auto_scan, " איתור אוטומטי"))

    def on_scan_error(self, err_msg):
        QMessageBox.critical(self, "שגיאה", str(err_msg))
        self._reset_btn(self.btn_auto_scan, " איתור אוטומטי")

    def _reset_btn(self, btn, text):
        btn.setText(text)
        btn.setEnabled(True)

    def _scan_yemot_folder(self, token, current_path, stats):
        found = []
        try:
            clean_path = current_path.strip("/")
            ext_path = f"ivr2:{clean_path}/ext.ini" if clean_path else "ivr2:/ext.ini"
            content = self._get_file_from_yemot(token, ext_path)
            if content and ("api_link" in content.lower() or "type=api" in content.lower().replace(" ", "")):
                if clean_path: found.append(clean_path)

            params = urllib.parse.urlencode({'token': token, 'path': current_path})
            req = urllib.request.Request(f"https://www.call2all.co.il/ym/api/GetIVR2Dir?{params}", headers={'User-Agent': USER_AGENT})
            with urllib.request.urlopen(req, timeout=10) as response:
                if response.status == 200:
                    data = json.loads(response.read().decode('utf-8', errors='ignore'))
                    if data.get("responseStatus") == "OK":
                        for d in data.get("dirs", []):
                            name = str(d.get("name", "")).strip() if isinstance(d, dict) else str(d).strip()
                            if name and name.lower() not in ["trash", "log", "tts", "messages", "macro"]:
                                found.extend(self._scan_yemot_folder(token, f"{current_path}/{name}" if current_path!="/" else name, stats))
        except: pass
        return found

    # =========================================================================
    # תהליכי שרת מקומי (HA & Cloudflare)
    # =========================================================================
    def start_ha(self):
        base_dir = self.hass_path_input.text().strip()
        if not base_dir:
            QMessageBox.warning(self, "שגיאה", "נתיב עבודה חסר.")
            return

        hass_cmd = os.path.join(base_dir, "hass.cmd")
        if not os.path.exists(hass_cmd):
            QMessageBox.critical(self, "שגיאה", "קובץ הפעלה חסר.")
            return

        if self.ha_process is None or self.ha_process.state() == QProcess.ProcessState.NotRunning:
            self.ha_process = QProcess(self)
            self.ha_process.setWorkingDirectory(base_dir)
            self.ha_process.finished.connect(self.on_ha_finished)
            self.ha_process.start("cmd.exe", ["/c", hass_cmd])
            self.update_ui_buttons()
            self.update_sleep_block_state()

    def on_ha_finished(self):
        self.ha_process = None
        self.update_ui_buttons()
        self.update_sleep_block_state()

    def stop_ha(self):
        if self.ha_process and self.ha_process.state() != QProcess.ProcessState.NotRunning:
            os.system(f"taskkill /f /t /pid {self.ha_process.processId()} >nul 2>&1")
            self.ha_process.kill()
            self.ha_process = None
            self.update_ui_buttons()
            self.update_sleep_block_state()

    def start_cf(self):
        base_dir = self.hass_path_input.text().strip()
        if not base_dir: return

        cf_exe = os.path.join(base_dir, "cloudflared.exe")
        if not os.path.exists(cf_exe): return

        if self.cf_process is None or self.cf_process.state() == QProcess.ProcessState.NotRunning:
            self.cf_process = QProcess(self)
            self.cf_process.setWorkingDirectory(base_dir) # חובה להגדיר תיקיית עבודה
            args = ["tunnel", "--protocol", "http2", "--url", "http://127.0.0.1:8123"]
            if os.path.exists(NETFREE_CERT): args.extend(["--origin-ca-pool", NETFREE_CERT])

            self.cf_output_buffer = "" # איפוס הזיכרון
            self.cf_process.readyReadStandardError.connect(self.read_cf_output)
            self.cf_process.readyReadStandardOutput.connect(self.read_cf_output)
            self.cf_process.finished.connect(self.on_cf_finished)
            self.cf_process.start(cf_exe, args)

            self.badge_cf.set_state("warning")
            self.update_ui_buttons()
            self.update_sleep_block_state()

    def on_cf_finished(self):
        self.cf_process = None
        self.current_url = ""
        self.cf_output_buffer = ""
        self.update_ui_buttons()
        self.update_sleep_block_state()

    def stop_cf(self):
        if self.cf_process and self.cf_process.state() != QProcess.ProcessState.NotRunning:
            os.system(f"taskkill /f /t /pid {self.cf_process.processId()} >nul 2>&1")
            self.cf_process.kill()
            self.cf_process = None
            self.current_url = ""
            self.cf_output_buffer = ""
            self.update_ui_buttons()
            self.update_sleep_block_state()

    def start_all(self):
        self.start_ha()
        self.start_cf()

    def stop_all(self):
        self.stop_ha()
        self.stop_cf()

    def read_cf_output(self):
        if self.cf_process:
            # הוספה ל-Buffer במקרה שהטקסט מגיע בחתיכות
            err_data = self.cf_process.readAllStandardError().data().decode("utf-8", errors="ignore")
            out_data = self.cf_process.readAllStandardOutput().data().decode("utf-8", errors="ignore")
            self.cf_output_buffer += err_data + out_data

        match = re.search(r"https://[a-zA-Z0-9-]+\.trycloudflare\.com", self.cf_output_buffer)
        if match:
            new_url = match.group(0)
            if new_url != self.current_url:
                self.current_url = new_url
                self.url_display.setText(new_url)
                self.badge_cf.set_state("online")
                self.update_ui_buttons()
                
                # סנכרון אוטומטי לענן יתבצע ברקע בלי להקפיא את הממשק!
                self.force_update_yemot(auto=True)
                
                # איפוס ה-Buffer לאחר מציאת הכתובת
                self.cf_output_buffer = "" 

    # =========================================================================
    # סנכרון נתונים ברקע (QThread)
    # =========================================================================
    def force_update_yemot(self, auto=False):
        if not self.current_url:
            if not auto: QMessageBox.warning(self, "מערכת", "לא קיים ממשק פעיל לסנכרון.")
            return
        
        self.btn_force_sync.setText(" מסנכרן...")
        self.btn_force_sync.setEnabled(False)
        
        # הרצה ב-Thread למניעת תקיעות
        worker = WorkerThread(self._update_yemot_wrapper, self.current_url)
        worker.finished.connect(lambda res: self.on_sync_finished(res, auto))
        worker.error.connect(lambda err: self.on_sync_error(err, auto))
        self.threads.append(worker)
        worker.start()

    def _update_yemot_wrapper(self, new_url):
        sys_num = self.yemot_num_input.text().strip()
        password = self.yemot_pass_input.text().strip()
        exts_raw = self.extensions_input.text().strip()

        if not sys_num or not password:
            return {"status": "error", "msg": "חסרים פרטי ימות המשיח."}

        token = f"{sys_num}:{password}"
        extensions = [ext.strip() for ext in exts_raw.split(",") if ext.strip()]
        if not extensions: return {"status": "error", "msg": "לא הוגדרו שלוחות."}

        updated_count = sum(1 for ext in extensions if self._update_ext_ini_link(token, f"ivr2:{ext}/ext.ini", new_url))
        
        return {"status": "success", "updated": updated_count, "total": len(extensions)}

    def on_sync_finished(self, result, auto):
        if result["status"] == "success":
            self.btn_force_sync.setText(" סונכרן בהצלחה")
            if not auto:
                if result["updated"] == result["total"]:
                    QMessageBox.information(self, "מערכת", "סנכרון הנתונים הושלם בהצלחה.")
                else:
                    QMessageBox.warning(self, "מערכת", f"סנכרון חלקי ({result['updated']}/{result['total']}).")
        else:
            self.btn_force_sync.setText(" שגיאה בסנכרון")
            if not auto: QMessageBox.warning(self, "שגיאה", result["msg"])
            
        QTimer.singleShot(2500, lambda: self._reset_btn(self.btn_force_sync, " סנכרון API"))

    def on_sync_error(self, err_msg, auto):
        self.btn_force_sync.setText(" שגיאה בסנכרון")
        if not auto: QMessageBox.critical(self, "שגיאה", str(err_msg))
        QTimer.singleShot(2500, lambda: self._reset_btn(self.btn_force_sync, " סנכרון API"))

    def _get_file_from_yemot(self, token, path):
        try:
            params = urllib.parse.urlencode({'token': token, 'path': path})
            req = urllib.request.Request(f"https://www.call2all.co.il/ym/api/DownloadFile?{params}", headers={'User-Agent': USER_AGENT})
            with urllib.request.urlopen(req, timeout=10) as response:
                if response.status == 200:
                    content = response.read().decode('utf-8', errors='ignore')
                    if content.strip().startswith('{') and '"responseStatus"' in content: return None
                    return content
        except: pass
        return None

    def _update_ext_ini_link(self, token, path, new_url):
        original = self._get_file_from_yemot(token, path)
        if original is None: return False
        if "api_link" in original:
            updated = re.sub(r"(api_link\s*=\s*)https?://[^/\r\n]+", rf"\g<1>{new_url}", original)
            return self._send_file_to_yemot(token, path, updated)
        else:
            if not original.endswith("\n") and original != "": original += "\n"
            return self._send_file_to_yemot(token, path, original + f"api_link={new_url}\n")

    def _send_file_to_yemot(self, token, path, contents):
        try:
            data = urllib.parse.urlencode({'token': token, 'what': path, 'contents': contents}).encode('utf-8')
            req = urllib.request.Request("https://www.call2all.co.il/ym/api/UploadTextFile", data=data, headers={'User-Agent': USER_AGENT})
            with urllib.request.urlopen(req, timeout=10) as response: return response.status == 200
        except: return False


    def is_startup_enabled(self):
        return os.path.exists(BAT_PATH)

    def toggle_startup(self, checked):
        if checked:
            try:
                script_path = os.path.abspath(sys.argv[0])
                cmd = f'@echo off\nstart "" "{script_path}" --autostart\n' if script_path.endswith(".exe") else f'@echo off\nstart "" "{sys.executable}" "{script_path}" --autostart\n'
                os.makedirs(STARTUP_DIR, exist_ok=True)
                with open(BAT_PATH, "w", encoding="utf-8") as f: f.write(cmd)
            except: self.chk_startup.setChecked(False)
        else:
            try:
                if os.path.exists(BAT_PATH): os.remove(BAT_PATH)
            except: self.chk_startup.setChecked(True)

    def closeEvent(self, event):
        self.stop_all()
        event.accept()

    def apply_styles(self):
        qss = """
        QMainWindow {
            background-color: #F8FAFC; 
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        }
        
        QScrollArea { border: none; background-color: transparent; }
        QLabel { color: #0F172A; }
        
        #main_title {
            font-size: 28px;
            font-weight: 800;
            color: #0F172A;
            letter-spacing: -0.5px;
        }
        
        #subtitle { 
            color: #64748B; 
            font-size: 14.5px; 
            font-weight: 500; 
        }
        
        #section_title {
            font-size: 15.5px;
            font-weight: 700;
            color: #334155;
            margin-bottom: 2px;
        }
        
        #card {
            background-color: #FFFFFF;
            border-radius: 16px;
            border: 1px solid #E2E8F0;
        }
        
        #row_title, #setting_label {
            font-size: 14.5px;
            font-weight: 700;
            color: #1E293B;
        }
        
        #row_desc {
            font-size: 13.5px;
            color: #64748B;
            font-weight: 500;
        }
        
        #url_display {
            background-color: #F8FAFC;
            color: #4338CA;
            border-radius: 8px;
            padding: 14px 16px;
            font-size: 14px;
            font-weight: 600;
            border: 1px dashed #CBD5E1;
            font-family: Consolas, monospace;
        }

        /* --- עיצוב אחיד לכל הכפתורים בחלון הראשי --- */
        QMainWindow QPushButton {
            font-weight: 600;
            font-size: 14px;
            border-radius: 20px;
            padding: 0 20px;
            border: 1px solid transparent;
            outline: none;
        }
        QMainWindow QPushButton:hover { cursor: pointinghand; }
        
        /* כפתור הפעלה ראשי גדול */
        #btn_master_start {
            background-color: #4F46E5;
            color: white;
            font-size: 15px;
            border-radius: 22px;
        }
        #btn_master_start:hover { background-color: #4338CA; }
        #btn_master_start:pressed { background-color: #3730A3; }
        
        /* כפתור כיבוי ראשי גדול (האדום החדש והרך) */
        #btn_master_stop {
            background-color: #E25555;
            color: white;
            font-size: 15px;
            border-radius: 22px;
        }
        #btn_master_stop:hover { background-color: #D64646; }
        #btn_master_stop:pressed { background-color: #C63C3C; }

        /* כפתורים ראשיים ברמת השרת */
        #btn_primary {
            background-color: #4F46E5;
            color: white;
        }
        #btn_primary:hover { background-color: #4338CA; }
        #btn_primary:pressed { background-color: #3730A3; }
        
        /* כפתורי עצירה/ניתוק (האדום החדש והרך) */
        #btn_danger {
            background-color: #E25555;
            color: white;
        }
        #btn_danger:hover { background-color: #D64646; }
        #btn_danger:pressed { background-color: #C63C3C; }
        
        /* כפתורי פעולה משניים (איתור, סנכרון, עיון) */
        #btn_outline {
            background-color: #EEF2FF;
            color: #4338CA;
            border: 1px solid #C7D2FE;
        }
        #btn_outline:hover { background-color: #E0E7FF; border-color: #A5B4FC; color: #3730A3; }
        #btn_outline:pressed { background-color: #C7D2FE; }
        #btn_outline:disabled { background-color: #F8FAFC; color: #94A3B8; border-color: #E2E8F0; }
        
        /* כפתור שמירה */
        #btn_save {
            background-color: #10B981;
            color: white;
            font-size: 15px;
            border-radius: 22px; 
            padding: 0 30px;
        }
        #btn_save:hover { background-color: #059669; }
        #btn_save:pressed { background-color: #047857; }

        /* --- שדות טקסט --- */
        QLineEdit {
            background-color: #F8FAFC;
            color: #0F172A;
            border: 1px solid #CBD5E1;
            border-radius: 10px; 
            padding: 0 12px;
            font-size: 14.5px;
            font-weight: 500;
        }
        QLineEdit:hover { border-color: #94A3B8; }
        QLineEdit:focus { 
            border-color: #6366F1; 
            background-color: #FFFFFF; 
        }

        /* ==========================================
           עיצוב תיבות דיאלוג מוקפד (QMessageBox)
           ========================================== */
        QMessageBox {
            background-color: #FFFFFF;
        }
        QMessageBox QLabel {
            color: #1E293B;
            font-size: 14.5px;
            font-weight: 500;
            padding: 10px 0;
        }
        QMessageBox QPushButton {
            background-color: #EEF2FF;
            color: #4F46E5;
            border: 1px solid #C7D2FE;
            border-radius: 6px;
            padding: 6px 20px;
            font-size: 13.5px;
            font-weight: bold;
            min-height: 24px;
            min-width: 60px;
        }
        QMessageBox QPushButton:hover {
            background-color: #E0E7FF;
        }
        QMessageBox QPushButton:pressed {
            background-color: #C7D2FE;
        }
        """
        self.setStyleSheet(qss)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    font = app.font()
    font.setStyleStrategy(QFont.StyleStrategy.PreferAntialias)
    app.setFont(font)
    
    window = SmartHomeControlPanel()
    window.show()
    sys.exit(app.exec())