import os
import cv2
import time
import logging

from PyQt5.QtWidgets import (
    QMainWindow, QWidget, QFileDialog, QListWidget, QListWidgetItem,
    QSlider, QLabel, QPushButton, QComboBox, QCompleter,
    QHBoxLayout, QVBoxLayout, QMessageBox, QProgressBar, QTextEdit,
    QCheckBox, QApplication, QSizePolicy, QSpacerItem
)

from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
from PyQt5.QtGui import QPixmap, QImage, QIcon
from PyQt5.QtWidgets import QAbstractItemView

from core.generator import MetadataGenerator
from utils.file_utils import extract_filename
from utils.user_settings import load_user_settings, save_user_settings
from config import get_api_keys, validate_keys, t, set_locale
from ui.result_window import ResultWindow
from ui.worker import Worker
from ui.file_item_widget import FileItemWidget
from ui.toolbar_widget import TopBarWidget
from api_clients.openai_client import get_openai_models
from api_clients.gemini_client import get_gemini_models
from api_clients.mistral_client import get_mistral_models
from api_clients.deepseek_client import get_deepseek_models
from api_clients.grok_client import get_grok_models
from api_clients.openrouter_client import get_openrouter_models
from utils.theme import apply_theme

# Маппинг языков выдачи
LANG_CHOICES = [
    ("en", "English"),
    ("ru", "Русский"),
    ("uk", "Українська"),
    ("zh", "中文"),
    ("es", "Español"),
    ("de", "Deutsch"),
    ("fr", "Français"),
    ("kk", "Қазақша"),
]

logger = logging.getLogger(__name__)


class MainWindow(QMainWindow):
    def __init__(self):
        self.user_settings = load_user_settings()
        super().__init__()
        self.setWindowTitle(t("app_title"))
        self.resize(900, 700)
        self.setAcceptDrops(True)

        # список путей к выбранным файлам
        self.paths = []

        central = QWidget()
        self.setCentralWidget(central)
        v = QVBoxLayout(central)

        locale = self.user_settings.get("locale", "en")
        set_locale(locale)  # установить локаль перед созданием topbar
        dark = self.user_settings.get("dark_mode", True)
        self.topbar = TopBarWidget(dark_mode=dark)
        self.topbar.settings_clicked.connect(self.open_settings)
        self.topbar.theme_changed.connect(self.on_toggle_theme)
        self.topbar.language_changed.connect(self.on_language_change)
        v.addWidget(self.topbar)

        # счетчик загруженных файлов
        self.lbl_count = QLabel(t("files_loaded").format(count=0))
        self.lbl_count.setObjectName("files_count_label")
        v.addWidget(self.lbl_count)

        # кнопка выбора файлов
        self.btn_select = QPushButton(t("btn_select"))
        self.btn_select.clicked.connect(self.select_files)
        v.addWidget(self.btn_select)

        # список файлов
        self.list_files = QListWidget()
        self.list_files.setViewMode(QListWidget.ListMode)
        self.list_files.setResizeMode(QListWidget.Adjust)
        self.list_files.setSpacing(5)

        # включаем drag-and-drop и режим только для дропа
        self.list_files.setAcceptDrops(True)
        self.list_files.setDragDropMode(QAbstractItemView.DropOnly)

        # прокидываем события на сам список и на его viewport,
        # потому что именно viewport "съедает" события перетаскивания
        for w in (self.list_files, self.list_files.viewport()):
            w.setAcceptDrops(True)
            w.dragEnterEvent   = self.dragEnterEvent
            w.dragMoveEvent    = self.dragMoveEvent
            w.dragLeaveEvent   = self.dragLeaveEvent
            w.dropEvent        = self.dropEvent

        v.addWidget(self.list_files)

        # оверлей при перетаскивании
        self.overlay = QLabel(self.list_files.viewport())
        self.overlay.setObjectName("drop_overlay")
        self.overlay.setAlignment(Qt.AlignCenter)
        self.overlay.setText(t("overlay_drop_files"))
        self.overlay.hide()

        # метка предупреждений
        self.warn_lbl = QLabel()
        self.warn_lbl.setStyleSheet("color: red; font-weight: bold;")
        v.addWidget(self.warn_lbl)

        # слайдеры
        sliders_layout = QVBoxLayout()
        # Title
        row_title = QHBoxLayout()
        self.lbl_title = QLabel(t("label_title_len"))
        self.slider_title = QSlider(Qt.Horizontal)
        self.slider_title.setRange(80, 130)
        self.slider_title.setValue((80 + 130) // 2)
        self.value_title = QLabel(str(self.slider_title.value()))
        self.slider_title.valueChanged.connect(lambda v: self.value_title.setText(str(v)))
        row_title.addWidget(self.lbl_title)
        row_title.addWidget(self.slider_title, stretch=1)
        row_title.addWidget(self.value_title)
        sliders_layout.addLayout(row_title)
        # Description
        row_desc = QHBoxLayout()
        self.lbl_desc = QLabel(t("label_desc_len"))
        self.slider_desc = QSlider(Qt.Horizontal)
        self.slider_desc.setRange(100, 150)
        self.slider_desc.setValue((100 + 150) // 2)
        self.value_desc = QLabel(str(self.slider_desc.value()))
        self.slider_desc.valueChanged.connect(lambda v: self.value_desc.setText(str(v)))
        row_desc.addWidget(self.lbl_desc)
        row_desc.addWidget(self.slider_desc, stretch=1)
        row_desc.addWidget(self.value_desc)
        sliders_layout.addLayout(row_desc)
        # Tags
        row_tags = QHBoxLayout()
        self.lbl_tags = QLabel(t("label_tags_len"))
        self.slider_tags = QSlider(Qt.Horizontal)
        self.slider_tags.setRange(1, 97)
        self.slider_tags.setValue((1 + 97) // 2)
        self.value_tags = QLabel(str(self.slider_tags.value()))
        self.slider_tags.valueChanged.connect(lambda v: self.value_tags.setText(str(v)))
        row_tags.addWidget(self.lbl_tags)
        row_tags.addWidget(self.slider_tags, stretch=1)
        row_tags.addWidget(self.value_tags)
        sliders_layout.addLayout(row_tags)
        v.addLayout(sliders_layout)

        # выбор сервиса
        svc_h = QHBoxLayout()
        self.lbl_svc = QLabel(t("svc_label"))
        svc_h.addWidget(self.lbl_svc)
        self.combo_svc = QComboBox()
        svc_h.addWidget(self.combo_svc)
        svc_h.addStretch()
        v.addLayout(svc_h)

        self.combo_model = QComboBox()
        self.combo_model.setEditable(True)
        self.combo_model.setInsertPolicy(QComboBox.NoInsert)
        # Получаем дефолтный комплитер от самого QComboBox:
        completer = self.combo_model.completer()
        completer.setFilterMode(Qt.MatchContains)
        completer.setCompletionMode(QCompleter.PopupCompletion)
        self.combo_model.setCompleter(completer)
        self.combo_model.setPlaceholderText("🔍 Поиск модели…")
        self.combo_model.hide()
        v.addWidget(self.combo_model)

        self.model_desc = QLabel()
        self.model_desc.setStyleSheet("color: gray; font-style: italic;")
        self.model_desc.hide()
        v.addWidget(self.model_desc)

        # выбор платформы (Adobe / Shutterstock)
        self.topbar.platform_changed.connect(self.on_platform_change)
        self._platform = "adobe"  # по умолчанию

        # ВЫПАДАЮЩИЙ СПИСОК — ЯЗЫК ГЕНЕРАЦИИ МЕТАДАННЫХ
        self.lbl_gen_lang = QLabel(t("label_gen_lang"))
        self.combo_gen_lang = QComboBox()
        for code, label in LANG_CHOICES:
            self.combo_gen_lang.addItem(label, code)
        # восстановление выбора из настроек
        gen_lang = self.user_settings.get("gen_lang", "en")
        idx = self.combo_gen_lang.findData(gen_lang)
        if idx >= 0:
            self.combo_gen_lang.setCurrentIndex(idx)
        self.combo_gen_lang.currentIndexChanged.connect(self.on_gen_lang_changed)
        v.addWidget(self.lbl_gen_lang)
        v.addWidget(self.combo_gen_lang)

        # прогресс и лог
        self.progress_bar = QProgressBar()
        v.addWidget(self.progress_bar)
        self.log_area = QTextEdit()
        self.log_area.setReadOnly(True)
        v.addWidget(self.log_area, stretch=1)

        # Выбор анализатора изображений
        self.lbl_vision = QLabel(t("label_vision"))
        self.combo_vision = QComboBox()
        self.combo_vision.addItems(["Gemini", "OpenAI"])
        v.addWidget(self.lbl_vision)
        v.addWidget(self.combo_vision)

        # --- Восстановление выбора анализатора изображений ---
        vision = self.user_settings.get("vision", "Gemini")
        idx_vision = self.combo_vision.findText(vision)
        if idx_vision >= 0:
            self.combo_vision.setCurrentIndex(idx_vision)
        self.combo_vision.currentIndexChanged.connect(self.on_vision_changed)

        # кнопка генерации
        self.btn_gen = QPushButton(t("btn_generate"))
        self.btn_gen.clicked.connect(self.generate)
        v.addWidget(self.btn_gen)

        # Применяем язык
        locale = self.user_settings.get("locale", "en")
        idx = self.topbar.lang_combo.findData(locale)
        if idx >= 0:
            self.topbar.lang_combo.setCurrentIndex(idx)

        # Значения слайдеров
        self.slider_title.setValue(self.user_settings.get("title_len", 100))
        self.slider_desc.setValue(self.user_settings.get("desc_len", 120))
        self.slider_tags.setValue(self.user_settings.get("tags_len", 50))
        self.slider_title.valueChanged.connect(self.save_sliders)
        self.slider_desc.valueChanged.connect(self.save_sliders)
        self.slider_tags.valueChanged.connect(self.save_sliders)

        # Платформа (Adobe / Shutterstock)
        platform = self.user_settings.get("platform", "adobe")
        self.topbar.set_platform(platform)

        # финализация темы и локализации
        self.retranslate_ui()
        self.refresh_services()
        self.topbar.update_texts()

        # === ВОССТАНОВЛЕНИЕ ВЫБОРА СЕРВИСА ===
        service = self.user_settings.get("service", "Gemini")
        svc_idx = self.combo_svc.findText(service)
        if svc_idx >= 0:
            self.combo_svc.blockSignals(True)
            self.combo_svc.setCurrentIndex(svc_idx)
            self.combo_svc.blockSignals(False)
            self.on_service_changed(service)
        else:
            self.on_service_changed(self.combo_svc.currentText())
        # Подключай сигнал ТОЛЬКО после восстановления!
        self.combo_svc.currentTextChanged.connect(self.on_service_changed)

        # Выбор модели — только после того, как combo_model заполнен (можно через QTimer.singleShot(0, ...) если combo_model заполняется асинхронно)
        def set_model():
            model = self.user_settings.get("model", "gemini-1.5-flash")
            idx = self.combo_model.findData(model)
            if idx >= 0:
                self.combo_model.setCurrentIndex(idx)
        from PyQt5.QtCore import QTimer
        QTimer.singleShot(0, set_model)

    def on_gen_lang_changed(self, idx):
        lang = self.combo_gen_lang.itemData(idx)
        self.user_settings["gen_lang"] = lang
        save_user_settings(self.user_settings)

    def on_vision_changed(self, idx):
        vision = self.combo_vision.itemText(idx)
        self.user_settings["vision"] = vision
        save_user_settings(self.user_settings)

    def on_toggle_theme(self, dark: bool):
        self.user_settings["dark_mode"] = dark
        save_user_settings(self.user_settings)
        apply_theme(dark)
        theme_file = "styles_dark.qss" if dark else "styles.qss"
        qss_path = os.path.join(os.path.dirname(__file__), theme_file)
        try:
            with open(qss_path, encoding="utf-8") as f:
                qss = f.read()
            QApplication.instance().setStyleSheet(qss)
            logger.info(f"Тема применена: {qss_path}")
        except Exception as e:
            logger.error(f"Не удалось загрузить тему {qss_path}: {e}")

    def on_language_change(self, locale: str):
        set_locale(locale)
        self.user_settings["locale"] = locale
        save_user_settings(self.user_settings)
        self.retranslate_ui()

    def retranslate_ui(self):
        """
        Обновляет все тексты в окне согласно текущей локали.
        """
        self.setWindowTitle(t("app_title"))
        self.topbar.dark_mode.setText(t("label_dark_mode"))
        self.btn_select.setText(t("btn_select"))
        self.lbl_title.setText(t("label_title_len"))
        self.lbl_desc.setText(t("label_desc_len"))
        self.lbl_tags.setText(t("label_tags_len"))
        self.lbl_svc.setText(t("svc_label"))
        self.btn_gen.setText(t("btn_generate"))
        self.lbl_count.setText(t("files_loaded").format(count=len(self.paths)))
        self.lbl_vision.setText(t("label_vision"))
        self.lbl_gen_lang.setText(t("label_gen_lang")) 
        self.topbar.update_texts()

    def refresh_services(self):
        """
        Заполняет список доступных сервисов в ComboBox после смены ключей.
        """
        self.combo_svc.clear()
        # порядок совпадает с тем, что обрабатывает MetadataGenerator
        services = ["Gemini", "OpenAI", "Mistral", "DeepSeek", "Grok", "OpenRouter"]
        self.combo_svc.addItems(services)
        # при желании можно проверить, для каких из них есть API-ключи,
        # и пометить отсутствующие как disabled:
        keys = validate_keys()
        for i, svc in enumerate(services):
            # допустим, GEMINI_API_KEY — для Gemini и т.д.
            key_name = f"{svc.upper()}_API_KEY"
            if key_name in keys:
                self.combo_svc.model().item(i).setEnabled(False)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            self.overlay.setGeometry(self.list_files.viewport().rect())
            self.overlay.show()
            self.overlay.raise_()
            event.acceptProposedAction()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            self.overlay.setGeometry(self.list_files.viewport().rect())
            self.overlay.show()
            event.acceptProposedAction()
        else:
            event.ignore()

    def dragLeaveEvent(self, event):
        self.overlay.hide()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
        dropped_files = []
        for url in event.mimeData().urls():
            path = url.toLocalFile()
            if os.path.isfile(path):
                ext = os.path.splitext(path)[1].lower()
                if ext in ['.png', '.jpg', '.jpeg', '.bmp', '.webp', '.avif', '.gif', '.mp4', '.mov', '.avi']:
                    dropped_files.append(path)

        self.overlay.hide()
        if not dropped_files:
            return
        for f in dropped_files:
            if f not in self.paths:
                self.paths.append(f)
        self._update_file_list()

    def select_files(self):
        all_filter = t("filter_all")
        image_filter = t("filter_images")
        video_filter = t("filter_videos")
        files, _ = QFileDialog.getOpenFileNames(
            self,
            t("btn_select"),
            "",
            f"{all_filter};;{image_filter};;{video_filter}"
        )
        if not files:
            return
        for f in files:
            if f not in self.paths:
                self.paths.append(f)
        self._update_file_list()

    def _update_file_list(self):
        self.list_files.clear()
        for path in self.paths:
            item = QListWidgetItem()
            item.setData(Qt.UserRole, path)
            rec = {"path": path, "filename": os.path.basename(path)}
            widget = FileItemWidget(
                path=rec["path"],
                filename=rec["filename"],
                on_delete=self._remove_item
            )
            item.setSizeHint(widget.sizeHint())
            self.list_files.addItem(item)
            self.list_files.setItemWidget(item, widget)
        self.lbl_count.setText(t("files_loaded").format(count=len(self.paths)))

    def _remove_item(self, path: str):
        if path in self.paths:
            self.paths.remove(path)
        self._update_file_list()

    def generate(self):
        keys = get_api_keys()
        if not keys.get("GEMINI_API_KEY") and not keys.get("OPENAI_API_KEY"):
            QMessageBox.critical(
                self,
                t("missing_api_keys_title"),
                t("error_gemini_required")
            )
            return

        paths = [self.list_files.item(i).data(Qt.UserRole) for i in range(self.list_files.count())]
        if not paths:
            QMessageBox.information(
                self,
                t("error_no_files_title"),
                t("error_no_files_body")
            )
            return

        self.btn_select.setEnabled(False)
        self.btn_gen.setEnabled(True)
        self.log_area.clear()
        self.progress_bar.setValue(0)

        model = self.combo_model.currentData()
        vision = self.combo_vision.currentText()
        gen_lang = self.combo_gen_lang.currentData()
        self.worker = Worker(
            paths=paths,
            service=self.combo_svc.currentText(),
            title_len=self.slider_title.value(),
            desc_len=self.slider_desc.value(),
            tags_len=self.slider_tags.value(),
            platform=self._platform,
            model=model,
            vision=vision,
            gen_lang=gen_lang,
        )

        self.worker.progress.connect(self.progress_bar.setValue)
        self.worker.log.connect(self.log_area.append)
        self.worker.error.connect(self.handle_error)
        self.worker.finished.connect(self.on_finished)
        self.worker.start()

    def handle_error(self, msg: str):
        logger.error("Error in Worker: %s", msg)
        QMessageBox.critical(self, t("missing_api_keys_title"), msg)
        self.btn_select.setEnabled(True)
        self.btn_gen.setEnabled(True)

    def on_finished(self, records: list):
        self.log_area.append("✅ " + t("result_window_title"))
        self.result = ResultWindow(
            records,
            title_len=self.slider_title.value(),
            desc_len=self.slider_desc.value(),
            tags_len=self.slider_tags.value(),
            platform=self._platform
        )
        self.result.show()
        self.close()

    def open_settings(self):
        from ui.settings_window import SettingsWindow
        self.settings_win = SettingsWindow()
        self.topbar.language_changed.connect(self.settings_win.retranslate_ui)
        self.settings_win.keys_updated.connect(self.refresh_services)
        self.settings_win.show()

    def on_platform_change(self, name: str):
        self._platform = name
        self.user_settings["platform"] = name
        save_user_settings(self.user_settings)

    def on_service_changed(self, svc):
        self.user_settings["service"] = svc
        save_user_settings(self.user_settings)
        models_map = {
            "OpenAI": get_openai_models(),
            "Gemini": get_gemini_models(),
            "Mistral": get_mistral_models(),
            "DeepSeek": get_deepseek_models(),
            "Grok": get_grok_models(),
            "OpenRouter": get_openrouter_models(),
        }
        if svc in models_map:
            models = models_map[svc]
            self.combo_model.clear()
            for key, label in models.items():
                self.combo_model.addItem(key, key)  # itemText = название, data = id
            self.combo_model.show()
            self.model_desc.setText(models.get(self.combo_model.currentData(), ""))
            self.model_desc.show()
            self.combo_model.currentIndexChanged.connect(lambda idx: self.update_model_description(svc))
            self.update_model_description(svc)
        else:
            self.combo_model.hide()
            self.model_desc.hide()

    def update_model_description(self, svc):
        models_map = {
            "OpenAI": get_openai_models(),
            "Gemini": get_gemini_models(),
            "Mistral": get_mistral_models(),
            "DeepSeek": get_deepseek_models(),
            "Grok": get_grok_models(),
        }
        model_key = self.combo_model.currentData()
        self.user_settings["model"] = model_key
        save_user_settings(self.user_settings)
        desc = models_map.get(svc, {}).get(model_key, "")
        self.model_desc.setText(desc)

    def save_sliders(self, *args):
        self.user_settings["title_len"] = self.slider_title.value()
        self.user_settings["desc_len"] = self.slider_desc.value()
        self.user_settings["tags_len"] = self.slider_tags.value()
        save_user_settings(self.user_settings)