import json
from os import path
from qgis.PyQt import uic, QtWidgets
from qgis.PyQt.QtCore import QDateTime, Qt
from qgis.PyQt.QtSql import QSqlDatabase, QSqlQuery

FORM_CLASS, _ = uic.loadUiType(path.join(path.dirname(__file__), 'res', 'DialogLicTvs.ui'))
FORM_CLASS_2, _ = uic.loadUiType(path.join(path.dirname(__file__), 'res', 'DialogDemoMode.ui'))
FORM_CLASS_3, _ = uic.loadUiType(path.join(path.dirname(__file__), 'res', 'DialogSaveKey.ui'))

class DialogLicTvs(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        super(DialogLicTvs, self).__init__(parent)
        self.setupUi(self)

class DialogDemoMode(QtWidgets.QDialog, FORM_CLASS_2):
    def __init__(self, parent=None):
        super(DialogDemoMode, self).__init__(parent)
        self.setupUi(self)

class DialogSaveKey(QtWidgets.QDialog, FORM_CLASS_3):
    def __init__(self, parent=None):
        super(DialogSaveKey, self).__init__(parent)
        self.setupUi(self)

# Constants for license mode
MODE_DEMO = -1
MODE_ACTIVATED = 1

class LicTvs():
    def __init__(self, pathToDemoInitFile):
        self.dialogLicTvs = DialogLicTvs()
        self.dialogLicTvs.pushButton_3.clicked.connect(self._checkKeyStatButClicked)
        self.dialogLicTvs.pushButton_2.clicked.connect(self._actvateKey)
        self.dialogLicTvs.pushButton_4.clicked.connect(self._saveKey)
        self.dialogLicTvs.lineEdit_3.textChanged.connect(self.keyStatChanged)

        self.dialogDemoMode = DialogDemoMode()

        self.dialogSaveKey = DialogSaveKey()

        self.error = ''
        self.keyInfo = {}
        self.pamDict = {}

        self.pamTvsFlag = False

        self.licMode = MODE_DEMO # demo mode
        self.pathToDemoInitFile = pathToDemoInitFile
        self.config_path = path.join(path.dirname(__file__), 'res', 'pam.tvs')
        self.currServerStatus = 'Disconnected'
        self.keyStatusDic = {
            -1: 'Invalid key',
            0: 'Created',
            1: 'Waiting for activation',
            2: 'Activated',
            3: 'Expired',
            4: 'Blocked',
            5: 'Deleted'
        }

        self._load_config()

        if "QODBC" not in QSqlDatabase.drivers():
            self.error = f"ERROR: Драйвер QODBC не найден!"
        
        self.initModuleContent = self._getInitFileFromLicServer()

    def _load_config(self):
        #config_path = path.join(path.dirname(__file__), 'res', 'pam.tvs')
        if not path.exists(self.config_path):
            self.error = "ERROR: Конфигурационный файл не найден"
            return False
        
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                self.pamDict = json.load(f)
            self.pamTvsFlag = True
            try:
                key_val = int(self.pamDict['Client'].get('Key', 0))
            except Exception:
                key_val = 0
            self._getKeyStatus(key_val)
        except Exception as e:
            self.error = f"ERROR: Ошибка чтения JSON: {e}"

    def _openBd(self):
        if not self.pamTvsFlag:
            self.error = "ERROR: Конфиг не загружен"
            return False
        
        if "QODBC" not in QSqlDatabase.drivers():
            self.error = "ERROR: Драйвер QODBC недоступен"
            return False


        conn_name = "TvsLicenseConn"

        try:
            if QSqlDatabase.contains(conn_name):
                QSqlDatabase.removeDatabase(conn_name)
        except Exception:
            # removeDatabase can raise if connection is in use; log and continue
            print("Could not remove existing connection (may not exist)")


        db = QSqlDatabase.addDatabase("QODBC", conn_name)

        logName = self.pamDict.get('Client', {}).get('User')
        pwdBd = self.pamDict.get('Client', {}).get('Psw')

        servers = self.pamDict.get('Servers', {})
        if not isinstance(servers, dict):
            self.error = "ERROR: No license servers configured"
            # Clean up DB connection
            db.close()
            QSqlDatabase.removeDatabase(conn_name)
            return False

        for serverName, value in servers.items():
            connection_string = (
                f"DRIVER={{SQL Server}};"
                f"SERVER={value['IP']},{value['Port']};"
                f"DATABASE={value['Db']};"
                f"UID={logName};PWD={pwdBd};"
                f"Connection Timeout=5;" # Чтобы не ждать вечно
            )
            db.setDatabaseName(connection_string)
            if db.open():
                self.db = db
                self.currServerStatus = 'Current license server: ' + serverName
                return True
            
        self.currServerStatus = 'License servers unavailable!'
        self.error = f"ERROR: Ошибка соединения: {self.db.lastError().text()}"
        try:
            db.close()
        except Exception:
            pass
        try:
            QSqlDatabase.removeDatabase(conn_name)
        except Exception:
            print("Could not remove database connection during cleanup")
        return False
    
    def _close_and_remove_db(self, conn_name="TvsLicenseConn"):
        try:
            if hasattr(self, 'db'):
                try:
                    self.db.close()
                except Exception:
                    pass
            QSqlDatabase.removeDatabase(conn_name)
        except Exception:
            print("Error removing database connection")

    def _getKeyStatus(self, clientKey):
        #print(clientKey)
        self.keyInfo = {'Key':clientKey}
        try:
            clientKey = int(clientKey)
        except:
            self.error = "ERROR: Некорректный формат лицензионного клча"
            self.licMode = MODE_DEMO

        if not self._openBd():
            self.licMode = MODE_DEMO
            return False

        conn_name = "TvsLicenseConn"
        query = QSqlQuery(self.db)
        try:
            query.prepare("{CALL [dbo].[GetKeyInfo] (?)}")
            query.addBindValue(clientKey)
        except Exception as e:
            self.error = f"ERROR: Failed to prepare query: {e}"
            self._close_and_remove_db(conn_name)
            self.licMode = MODE_DEMO
            return False
        
        if not query.exec_():
            err = query.lastError().text() if hasattr(query, 'lastError') else 'Unknown'
            self.error = f"ERROR: Ошибка выполнения: {err}"
            self._close_and_remove_db(conn_name)
            self.licMode = MODE_DEMO
            return False

        # Обработка результатов запроса
        if query.next():
            try:
                rec = query.record()
                self.keyInfo = {rec.fieldName(i): query.value(i) for i in range(rec.count())}

                status_id = self.keyInfo.get('id_Status', -1)
                program_name = self.pamDict['Client'].get('ProgramName', '')
                progCorrectFlag = self.keyInfo.get('Program', '1') == program_name

                if not progCorrectFlag:
                    status_id = -1
                    self.keyInfo['id_Status'] = -1

                self.licMode = MODE_ACTIVATED if status_id == 2 and progCorrectFlag else MODE_DEMO

            except Exception as e:
                self.error = f"ERROR: Ошибка обработки результата: {e}"
                self.licMode = MODE_DEMO
                self._close_and_remove_db(conn_name)
                return False
        else:
            self.error = "ERROR: Key not found in DB"
            self.licMode = MODE_DEMO
        self._close_and_remove_db(conn_name)
        
        return True
    
    def _initDlgLicTvs(self):
        self.dialogLicTvs.lineEdit_7.setText(self.currServerStatus)
        self.dialogLicTvs.lineEdit.setText(self.keyInfo.get('Program', ''))
        self.dialogLicTvs.lineEdit_8.setText(self.pamDict.get('Client', {}).get('ProgramName', ''))
        self.dialogLicTvs.lineEdit_4.setText(self.keyInfo.get('User_Name', ''))
        self.dialogLicTvs.lineEdit_5.setText(self.keyInfo.get('User_Org', ''))
        self.dialogLicTvs.lineEdit_2.setText(str(self.keyInfo.get('Key', '')))

        status_id = self.keyInfo.get('id_Status', -1)
        self.dialogLicTvs.lineEdit_3.setText(self.keyStatusDic.get(status_id, 'Invalid key'))

        dt = self.keyInfo.get('DateOfExpiration')
        if isinstance(dt, QDateTime) and dt.isValid():
            self.dialogLicTvs.lineEdit_6.setText(dt.toString("yyyy-MM-dd"))
        else:
            self.dialogLicTvs.lineEdit_6.setText(str(dt) if dt else "")
        
        # Управление кнопками
        self.dialogLicTvs.pushButton_2.setEnabled(status_id == 1) # Активация
        if self.keyInfo.get('id_Status', -1) == 2 and str(self.keyInfo.get('Key', '1')) != str(self.pamDict['Client'].get('Key', 0)) and str(self.keyInfo.get('Program', '1')) == str(self.pamDict['Client'].get('ProgramName', '')):
            self.dialogLicTvs.pushButton_4.setEnabled(True)
        else:
            self.dialogLicTvs.pushButton_4.setEnabled(False)

    def showDlgLicTvs(self):
        try:
            client_key = int(self.pamDict.get('Client', {}).get('Key', 0))
        except Exception:
            client_key = 0
        self._getKeyStatus(client_key)
        self._initDlgLicTvs()
        self.dialogLicTvs.exec()
    
    def _checkKeyStatButClicked(self):
        self._getKeyStatus(self.dialogLicTvs.lineEdit_2.text())
        self._initDlgLicTvs()
    
    def _actvateKey(self):
        if self.keyInfo.get('id_Status', -1) != 1:
            return False
        
        if not self._openBd():
            return False

        conn_name = "TvsLicenseConn"
        query = QSqlQuery(self.db)
        sql = """
            DECLARE @id_Status int;
            Exec [dbo].[ActivateKeyKPT] @Key = ?, @Program = ?, @User_Name = ?, @User_Org = ?, @id_Status = @id_Status OUT;
            SELECT @id_Status AS [Status];
            """
        #print(sql)
        try:
            query.prepare(sql)
            query.bindValue(0, int(self.keyInfo.get('Key', '1')))
            query.bindValue(1, self.keyInfo.get('Program', '1'))
            query.bindValue(2, self.keyInfo.get('User_Name', '1'))
            query.bindValue(3, self.keyInfo.get('User_Org', '1'))
        except Exception as e:
            self.error = f"ERROR: Ошибка подготовки запроса активации: {e}"
            self._close_and_remove_db(conn_name)
            return False
        
        resStatus = ''

        if query.exec_() and query.next():
            resStatus = query.value(0) 
            if not resStatus:
                resStatus = ''
        else:
            #print(query.lastError().text())
            resStatus = 'Not query'

        self._getKeyStatus(self.dialogLicTvs.lineEdit_2.text())
        self._initDlgLicTvs()
        self._close_and_remove_db(conn_name)
        self.showDlgSaveKey()
        #print(resStatus)
        
        return resStatus

    def _saveKey(self):
        self.pamDict['Client']['Key'] = self.dialogLicTvs.lineEdit_2.text()
        with open(path.join(self.config_path), "w") as file:
            json.dump(self.pamDict, file, indent=4)
        self.dialogLicTvs.pushButton_4.setEnabled(False)

    def _getDemoFile(self):
        if not path.exists(self.pathToDemoInitFile):
            return "ERROR: Demo file missing"
        try:
            with open(self.pathToDemoInitFile, encoding='utf-8') as f:
                return f.read()
        except: return 'ERROR: Ошибка чтения demo файла'
    
    def _getInitFileFromLicServer(self):
        
        if self.licMode != MODE_ACTIVATED:
            return self._getDemoFile()
        
        if not self._openBd():
            return self._getDemoFile()
        
        conn_name = "TvsLicenseConn"
        fileName = self.pamDict['Client'].get('NameFile', 'errorFileName')
        query = QSqlQuery(self.db)
        sql = """
            DECLARE @id_Status int;
            DECLARE @Cont varchar(MAX);

            -- Выполняем процедуру
            EXEC dbo.GetFileByName @Key = ?, @Name = ?, @id_Status = @id_Status OUT, @Cont = @Cont OUT;

            -- Вместо PRINT используем SELECT, чтобы Qt увидел данные
            SELECT @id_Status AS [Status], @Cont AS [Content];
            """
        
        try:
            query.prepare(sql)
            query.bindValue(0, self.keyInfo.get('Key', '1'))
            query.bindValue(1, fileName)
        except Exception as e:
            self._close_and_remove_db(conn_name)
            return self._getDemoFile()

        content = ''
        status_getFile = ''
        if query.exec_() and query.next():
            try:
                status_getFile = query.value(0) or ''
                content = query.value(1) or ''
            except Exception:
                content = ''
        else:
            content = ''
        
        self._close_and_remove_db(conn_name)
        if not content:
            if int(status_getFile) == -8:
                print('Лицензионный ключ от другого программного обеспечения')
            return self._getDemoFile()
        return content
        
    def showDlgDemoMode(self):
        if self.licMode == MODE_ACTIVATED:
            return True
        
        status_id = self.keyInfo.get('id_Status', -1)
        self.dialogDemoMode.lineEdit.setText(self.keyStatusDic.get(status_id, self.error))

        self.dialogDemoMode.textBrowser.setText(self.pamDict.get('Client', {}).get('demoDescr', 'Демоснтрационный режим программного модуля с ограниченным функционалом. Для разблокировки модуля введите лицензионный ключ.'))

        return self.dialogDemoMode.exec()

    def showDlgSaveKey(self):
        if self.keyInfo.get('id_Status', -1) == 2 and str(self.keyInfo.get('Key', '1')) != str(self.pamDict['Client'].get('Key', 0)) and str(self.keyInfo.get('Program', '1')) == str(self.pamDict['Client'].get('ProgramName', '')):
            if self.dialogSaveKey.exec():
                self._saveKey()

    def keyStatChanged(self):
        status_id = self.keyInfo.get('id_Status', -1)
        # Управление кнопками
        self.dialogLicTvs.pushButton_2.setEnabled(status_id == 1) # Активация
        if self.keyInfo.get('id_Status', -1) == 2 and str(self.keyInfo.get('Key', '1')) != str(self.pamDict['Client'].get('Key', 0)) and str(self.keyInfo.get('Program', '1')) == str(self.pamDict['Client'].get('ProgramName', '')):
            self.dialogLicTvs.pushButton_4.setEnabled(True)
        else:
            self.dialogLicTvs.pushButton_4.setEnabled(False)
        self.showDlgSaveKey()

""" lt = LicTvs(r'o:\1_Обмен\Бухарин И.А\PYTHON\QGIS_scripts\tvsServerLicense\ForDevONLY\testDemo.txt')
print(lt.licMode)
print(lt.error)
print(lt._getInitFileFromLicServer())
print(lt.keyInfo)
lt.showDlgLicTvs() """
