import sys
import hashlib
import secrets
import string
import asyncio
from zipfile import error

import aiohttp
from zxcvbn import zxcvbn
from PyQt5.QtWidgets import (
    QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,
    QCheckBox, QSpinBox, QTabWidget, QGroupBox, QMessageBox, QMenuBar, QAction,
    QTextEdit
)
from PyQt5.QtCore import Qt, QEvent, QThread, pyqtSignal


class Password:
    def __init__(self):
        pass
        self.ambiguous_chars = {'O', '0', 'I', 'l', '1', '|', 'S', '5'}

    def generate(self, length=12, use_uppercase=True, use_digits=True, use_symbols=True, exclude_ambiguous=True):
        characters = string.ascii_lowercase
        if use_uppercase:
            characters += string.ascii_uppercase
        if use_digits:
            characters += string.digits
        if use_symbols:
            characters += string.punctuation
        if exclude_ambiguous:
            characters += ''.join(c for c in characters if c not in self.ambiguous_chars)
        password = ''.join(secrets.choice(characters) for _ in range(length))
        return password

    def check_strength(self, password):
        result = zxcvbn(password)
        score = result['score']  # от 0 до 4
        feedback = result['feedback']
        return score, feedback

    async def check_leak(self, password):
        sha1pwd = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
        prefix, suffix = sha1pwd[:5], sha1pwd[5:]
        url = f'https://api.pwnedpasswords.com/range/{prefix}'

        async with aiohttp.ClientSession() as session:
            async with session.get(url) as resp:
                text = await resp.text()

        hashes = (line.split(':') for line in text.splitlines())
        for h, count in hashes:
            if h == suffix:
                return int(count)
        return 0


class PasswordManagerApp(QWidget):
    def __init__(self):
        super().__init__()
        self.password = Password()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Password Manager')
        self.setFixedSize(800, 450)  # Соотношение сторон 16:9

        self.initMenuBar()

        self.tabs = QTabWidget()
        self.tabs.addTab(self.createGeneratorTab(), "Генератор паролей")
        self.tabs.addTab(self.createCheckerTab(), "Проверка пароля")

        mainLayout = QVBoxLayout()
        mainLayout.setMenuBar(self.menuBar)
        mainLayout.addWidget(self.tabs)

        self.setLayout(mainLayout)
        self.applyStyle('Light')

    def initMenuBar(self):
        self.menuBar = QMenuBar(self)

        themeMenu = self.menuBar.addMenu('Темы')

        themes = ['Fusion', 'Windows', 'WindowsVista', 'Macintosh', 'Dark', 'Light']

        for theme in themes:
            action = QAction(theme, self)
            action.triggered.connect(lambda checked, t=theme: self.applyStyle(t))
            themeMenu.addAction(action)

    def applyStyle(self, themeName):
        if themeName == 'Fusion':
            QApplication.setStyle(themeName)
            self.setStyleSheet('')
        elif themeName == 'Dark':
            self.setStyleSheet(self.darkTheme())
        elif themeName == 'Light':
            self.setStyleSheet(self.lightTheme())
        else:
            QApplication.setStyle(themeName)
            self.setStyleSheet('')

    def darkTheme(self):
        return """
        QWidget {
            background-color: #2e2e2e;
            color: #ffffff;
            font-size: 14px;
        }
        QPushButton {
            background-color: #3c3c3c;
            border: none;
            padding: 5px;
        }
        QPushButton:hover {
            background-color: #555555;
        }
        QLineEdit, QTextEdit {
            background-color: #3c3c3c;
            border: 1px solid #5e5e5e;
            color: #ffffff;
        }
        """

    def lightTheme(self):
        return """
        QWidget {
            background-color: #f6f6f6;
            color: #000000;
            font-size: 14px;
        }
        QPushButton {
            background-color: #e0e0e0;
            border: none;
            padding: 5px;
        }
        QPushButton:hover {
            background-color: #c8c8c8;
        }
        QLineEdit, QTextEdit {
            background-color: #ffffff;
            border: 1px solid #c0c0c0;
            color: #000000;
        }
        """

    def createGeneratorTab(self):
        generatorTab = QWidget()
        layout = QVBoxLayout()

        optionsGroup = QGroupBox("Опции пароля")
        optionsLayout = QHBoxLayout()

        self.lengthLabel = QLabel("Длина:")
        self.lengthSpinBox = QSpinBox()
        self.lengthSpinBox.setRange(6, 128)
        self.lengthSpinBox.setValue(12)

        self.uppercaseCheck = QCheckBox("Включать Заглавные")
        self.uppercaseCheck.setChecked(True)
        self.digitsCheck = QCheckBox("Включать Цифры")
        self.digitsCheck.setChecked(True)
        self.symbolsCheck = QCheckBox("Включать Символы")
        self.symbolsCheck.setChecked(True)

        self.excludeAmbiguousCheck = QCheckBox("Без неоднозначных")
        self.excludeAmbiguousCheck.setChecked(True)

        optionsLayout.addWidget(self.lengthLabel)
        optionsLayout.addWidget(self.lengthSpinBox)
        optionsLayout.addWidget(self.uppercaseCheck)
        optionsLayout.addWidget(self.digitsCheck)
        optionsLayout.addWidget(self.symbolsCheck)
        optionsLayout.addWidget(self.excludeAmbiguousCheck)
        optionsGroup.setLayout(optionsLayout)

        self.generatedPasswordLabel = QLabel("Сгенерированный пароль:")
        self.generatedPasswordLine = QLineEdit()
        self.generatedPasswordLine.setReadOnly(True)
        self.generatedPasswordLine.setEchoMode(QLineEdit.Password)
        self.generatedPasswordLine.installEventFilter(self)

        self.generateButton = QPushButton("Сгенерировать")
        self.generateButton.clicked.connect(self.generatePassword)
        self.passwordInput.clicked.connect(self.generatePassword)

        self.copyButton = QPushButton("Скопировать в буфер")
        self.copyButton.clicked.connect(lambda: self.copyToClipboard(self.generatedPasswordLine.text()))

        layout.addWidget(optionsGroup)
        layout.addWidget(self.generatedPasswordLabel)
        layout.addWidget(self.generatedPasswordLine)
        layout.addWidget(self.generateButton)
        layout.addWidget(self.copyButton)
        layout.addStretch()

        generatorTab.setLayout(layout)
        return generatorTab

    def createCheckerTab(self):
        checkerTab = QWidget()
        layout = QVBoxLayout()

        self.passwordLabel = QLabel("Введите пароль для проверки:")
        self.passwordInput = QLineEdit()
        self.passwordInput.setEchoMode(QLineEdit.Password)
        self.passwordInput.installEventFilter(self)

        self.checkButton = QPushButton("Проверить пароль")
        self.checkButton.clicked.connect(self.checkPassword)

        self.copyInputButton = QPushButton("Скопировать пароль в буфер")
        self.copyInputButton.clicked.connect(lambda: self.copyToClipboard(self.passwordInput.text()))

        self.strengthLabel = QLabel("Сложность пароля:")
        self.strengthResult = QLabel("")

        self.feedbackLabel = QLabel("Рекомендации:")
        self.feedbackText = QTextEdit()
        self.feedbackText.setReadOnly(True)

        self.leakLabel = QLabel("Проверка утечек:")
        self.leakResult = QLabel("")
        self.leakButton = QPushButton("Проверить утечки")
        self.leakButton.clicked.connect(self.checkLeaksAsync)
        self.leakButton.setEnabled(False)

        layout.addWidget(self.passwordLabel)
        layout.addWidget(self.passwordInput)
        layout.addWidget(self.copyInputButton)
        layout.addWidget(self.checkButton)
        layout.addWidget(self.strengthLabel)
        layout.addWidget(self.strengthResult)
        layout.addWidget(self.feedbackLabel)
        layout.addWidget(self.feedbackText)
        layout.addWidget(self.leakLabel)
        layout.addWidget(self.leakResult)
        layout.addWidget(self.leakButton)
        layout.addStretch()

        checkerTab.setLayout(layout)
        return checkerTab

    def eventFilter(self, source, event):
        if event.type() == QEvent.Enter:
            if isinstance(source, QLineEdit):
                source.setEchoMode(QLineEdit.Normal)
        elif event.type() == QEvent.Leave:
            if isinstance(source, QLineEdit):
                source.setEchoMode(QLineEdit.Password)
        return super(PasswordManagerApp, self).eventFilter(source, event)

    def generatePassword(self):
        length = self.lengthSpinBox.value()
        use_uppercase = self.uppercaseCheck.isChecked()
        use_digits = self.digitsCheck.isChecked()
        use_symbols = self.symbolsCheck.isChecked()
        exclude_ambiguous = self.symbolsCheck.isChecked()

        password = self.password.generate(length=length, use_uppercase=use_uppercase,
                                          use_digits=use_digits, use_symbols=use_symbols,
                                          exclude_ambiguous=exclude_ambiguous)
        self.generatedPasswordLine.setText(password)
        self.passwordInput.setText(password)

    def copyToClipboard(self, text):
        clipboard = QApplication.clipboard()
        clipboard.setText(text)
        QMessageBox.information(self, "Скопировано", "Пароль скопирован в буфер обмена.")

    def checkPassword(self):
        password = self.passwordInput.text()
        if password == '':
            QMessageBox.warning(self, "Ошибка", "Введите пароль для проверки.")
            return

        score, feedback = self.password.check_strength(password)
        self.strengthResult.setText(f"{score}/4")
        feedback_text = '\n'.join(feedback.get('suggestions', []))
        self.feedbackText.setPlainText(feedback_text)
        self.leakButton.setEnabled(True)
        self.leakResult.setText("")

    def checkLeaksAsync(self):
        password = self.passwordInput.text()
        if password == '':
            QMessageBox.warning(self, "Ошибка", "Введите пароль для проверки.")
            return

        self.leakButton.setEnabled(False)
        self.leakResult.setText("Проверка...")

        self.thread = LeakCheckThread(password)
        self.thread.leak_found.connect(self.handleLeakResult)
        self.thread.error_occurred.connect(self.handleLeakError)
        self.thread.start()

    def handleLeakResult(self, count):
        if count == 0:
            self.leakResult.setText("Утечек не обнаружено.")
        else:
            self.leakResult.setText(f"Пароль найден {count} раз в утечках!")
        self.leakButton.setEnabled(True)

    def handleLeakError(self, error):
        QMessageBox.warning(self, "Ошибка", f"Произошла ошибка при проверке утечек: {error}")
        self.leakButton.setEnabled(True)
        self.leakResult.setText("")

    def closeEvent(self, event):
        try:
            if self.thread.isRunning():
                self.thread.quit()
                self.thread.wait()
        except AttributeError:
            pass
        event.accept()


class LeakCheckThread(QThread):
    leak_found = pyqtSignal(int)
    error_occurred = pyqtSignal(str)

    def __init__(self, password):
        super().__init__()
        self.password = password

    def run(self):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            count = loop.run_until_complete(self.checkPasswordLeak(self.password))
            self.leak_found.emit(count)
        except Exception as e:
            self.error_occurred.emit(str(e))

    async def checkPasswordLeak(self, password):
        sha1pwd = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
        prefix, suffix = sha1pwd[:5], sha1pwd[5:]
        url = f'https://api.pwnedpasswords.com/range/{prefix}'

        async with aiohttp.ClientSession() as session:
            async with session.get(url) as resp:
                if resp.status != 200:
                    raise Exception("Не удалось получить данные от сервера.")
                text = await resp.text()

        hashes = (line.split(':') for line in text.splitlines())
        for h, count in hashes:
            if h == suffix:
                return int(count)
        return 0


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_app = PasswordManagerApp()
    main_app.show()
    sys.exit(app.exec_())


C:\Users\user\PycharmProjects\pythonProject1\venv\Scripts\python.exe C:\Users\user\PycharmProjects\pythonProject1\dich2\main.py 
Traceback (most recent call last):
  File "C:\Users\user\PycharmProjects\pythonProject1\dich2\main.py", line 368, in <module>
    main_app = PasswordManagerApp()
  File "C:\Users\user\PycharmProjects\pythonProject1\dich2\main.py", line 62, in __init__
    self.initUI()
  File "C:\Users\user\PycharmProjects\pythonProject1\dich2\main.py", line 71, in initUI
    self.tabs.addTab(self.createGeneratorTab(), "Генератор паролей")
  File "C:\Users\user\PycharmProjects\pythonProject1\dich2\main.py", line 187, in createGeneratorTab
    self.passwordInput.clicked.connect(self.generatePassword)
AttributeError: 'PasswordManagerApp' object has no attribute 'passwordInput'