# -*- coding: utf-8 -*-
"""
LVT Disk Cleaner — Dọn dẹp dữ liệu ổ cứng & QGIS layers.

Scan thư mục theo từ khóa, hiển thị preview file matching,
xác nhận trước khi xóa, gỡ layer QGIS liên quan.

Author: Lộc Vũ Trung (LVT)
License: GPL-3.0
"""

import os
from qgis.PyQt.QtCore import Qt, QTimer
from qgis.PyQt.QtWidgets import (
    QDialog, QVBoxLayout, QHBoxLayout, QLabel,
    QPushButton, QFileDialog, QMessageBox, QGroupBox,
    QProgressBar, QApplication, QCheckBox, QLineEdit,
    QTreeWidget, QTreeWidgetItem, QHeaderView,
    QTextEdit,
)
from qgis.core import QgsProject
from ..shared.qt_compat import QtCompat, MsgButton


# ═══════════════════════════════════════════════════════════════
# Helpers
# ═══════════════════════════════════════════════════════════════

def _format_size(size_bytes):
    """Format bytes to human-readable size string."""
    if size_bytes < 1024:
        return f"{size_bytes} B"
    elif size_bytes < 1024 * 1024:
        return f"{size_bytes / 1024:.1f} KB"
    elif size_bytes < 1024 * 1024 * 1024:
        return f"{size_bytes / (1024 * 1024):.1f} MB"
    else:
        return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"


# ═══════════════════════════════════════════════════════════════
# Dialog class
# ═══════════════════════════════════════════════════════════════

class DiskCleanerDialog(QDialog):
    """Disk cleaner dialog — scan + preview + confirm + delete."""

    def __init__(self, iface, parent=None):
        super().__init__(parent or iface.mainWindow())
        self.iface = iface
        self.setWindowTitle("🧹 Disk Cleaner — Dọn dẹp dữ liệu")
        self.setMinimumSize(720, 650)
        self.setWindowFlags(
            self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
        self._scan_results = []  # list of (filepath, size, matched_keyword)
        self._matched_layers = []  # list of (layer_id, layer_name)
        self._build_ui()

    # ───────────────────────────────────────────────────────────
    # UI construction
    # ───────────────────────────────────────────────────────────

    def _build_ui(self):
        """Build the complete dialog UI."""
        ly = QVBoxLayout(self)

        # Header
        hdr = QLabel("<h3>🧹 Dọn dẹp dữ liệu ổ cứng & QGIS</h3>")
        ly.addWidget(hdr)

        # ── Input GroupBox ──
        grp_input = QGroupBox("📝 Thiết lập quét")
        vl_input = QVBoxLayout(grp_input)

        # Row 1: Keywords
        hl_kw = QHBoxLayout()
        hl_kw.addWidget(QLabel("Từ khóa:"))
        self.txt_keywords = QLineEdit()
        self.txt_keywords.setPlaceholderText(
            "Nhập từ khóa cách nhau bởi dấu phẩy (VD: chudan, khung, temp)")
        self.txt_keywords.setText("chudan, khung")
        hl_kw.addWidget(self.txt_keywords)
        vl_input.addLayout(hl_kw)

        # Row 2: Directory
        hl_dir = QHBoxLayout()
        hl_dir.addWidget(QLabel("Thư mục:"))
        self.txt_dir = QLineEdit()
        self.txt_dir.setPlaceholderText("Chọn thư mục cần quét")
        hl_dir.addWidget(self.txt_dir)
        self.btn_dir = QPushButton("📂")
        self.btn_dir.setFixedWidth(36)
        self.btn_dir.clicked.connect(self._browse_dir)
        hl_dir.addWidget(self.btn_dir)
        vl_input.addLayout(hl_dir)

        # Row 3: Options
        hl_opts = QHBoxLayout()
        self.chk_layers = QCheckBox("Gỡ layer QGIS khớp từ khóa")
        self.chk_layers.setChecked(True)
        self.chk_layers.setToolTip(
            "Gỡ layer đang mở trong QGIS có tên/nguồn chứa từ khóa")
        self.chk_disk = QCheckBox("Xóa file trên ổ cứng")
        self.chk_disk.setChecked(True)
        self.chk_subfolders = QCheckBox("Quét thư mục con")
        self.chk_subfolders.setChecked(True)
        hl_opts.addWidget(self.chk_layers)
        hl_opts.addWidget(self.chk_disk)
        hl_opts.addWidget(self.chk_subfolders)
        hl_opts.addStretch()
        vl_input.addLayout(hl_opts)

        # Row 4: File extensions filter
        hl_ext = QHBoxLayout()
        hl_ext.addWidget(QLabel("Lọc đuôi:"))
        self.txt_extensions = QLineEdit()
        self.txt_extensions.setPlaceholderText(
            "Để trống = tất cả. VD: .shp, .dbf, .shx, .prj, .cpg")
        hl_ext.addWidget(self.txt_extensions)
        vl_input.addLayout(hl_ext)

        # Scan button
        hl_scan = QHBoxLayout()
        self.btn_scan = QPushButton("🔍 Quét (Scan)")
        self.btn_scan.setMinimumHeight(34)
        self.btn_scan.setStyleSheet(
            "QPushButton { font-weight: bold; font-size: 13px; "
            "background-color: #1565c0; color: white; border-radius: 4px; }"
            "QPushButton:hover { background-color: #1976d2; }"
        )
        self.btn_scan.clicked.connect(self._scan)
        hl_scan.addWidget(self.btn_scan)
        vl_input.addLayout(hl_scan)

        ly.addWidget(grp_input)

        # ── Results GroupBox ──
        grp_results = QGroupBox("📊 Kết quả quét")
        vl_results = QVBoxLayout(grp_results)

        # Summary label
        self.lbl_summary = QLabel("Chưa quét.")
        self.lbl_summary.setStyleSheet("color: #666; font-style: italic;")
        vl_results.addWidget(self.lbl_summary)

        # Layers tree (matching QGIS layers)
        self.lbl_layers = QLabel("📌 Layer QGIS khớp từ khóa:")
        self.lbl_layers.setVisible(False)
        vl_results.addWidget(self.lbl_layers)

        self.tree_layers = QTreeWidget()
        self.tree_layers.setHeaderLabels(["Layer Name", "Source"])
        self.tree_layers.setColumnCount(2)
        header_l = self.tree_layers.header()
        header_l.setStretchLastSection(True)
        header_l.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
        self.tree_layers.setMaximumHeight(100)
        self.tree_layers.setVisible(False)
        vl_results.addWidget(self.tree_layers)

        # Files tree
        self.lbl_files = QLabel("📁 File trên ổ cứng khớp từ khóa:")
        self.lbl_files.setVisible(False)
        vl_results.addWidget(self.lbl_files)

        self.tree_files = QTreeWidget()
        self.tree_files.setHeaderLabels([
            "File", "Size", "Từ khóa khớp", "Thư mục"
        ])
        self.tree_files.setColumnCount(4)
        header_f = self.tree_files.header()
        header_f.setStretchLastSection(True)
        header_f.setSectionResizeMode(0, QHeaderView.ResizeMode.Interactive)
        header_f.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
        header_f.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
        self.tree_files.setMinimumHeight(150)
        self.tree_files.setVisible(False)
        vl_results.addWidget(self.tree_files)

        # Select all / deselect all
        hl_sel = QHBoxLayout()
        self.btn_select_all = QPushButton("☑ Chọn tất cả")
        self.btn_select_all.clicked.connect(self._select_all)
        self.btn_select_all.setVisible(False)
        self.btn_deselect_all = QPushButton("☐ Bỏ chọn tất cả")
        self.btn_deselect_all.clicked.connect(self._deselect_all)
        self.btn_deselect_all.setVisible(False)
        hl_sel.addWidget(self.btn_select_all)
        hl_sel.addWidget(self.btn_deselect_all)
        hl_sel.addStretch()
        vl_results.addLayout(hl_sel)

        ly.addWidget(grp_results)

        # ── Delete button ──
        self.btn_delete = QPushButton("🗑️ XÓA CÁC FILE ĐÃ CHỌN")
        self.btn_delete.setMinimumHeight(40)
        self.btn_delete.setStyleSheet(
            "QPushButton { font-size: 14px; font-weight: bold; "
            "background-color: #c62828; color: white; border-radius: 4px; }"
            "QPushButton:hover { background-color: #e53935; }"
            "QPushButton:disabled { background-color: #9e9e9e; }"
        )
        self.btn_delete.clicked.connect(self._delete)
        self.btn_delete.setVisible(False)
        ly.addWidget(self.btn_delete)

        # ── Progress bar (hidden) ──
        self.progress = QProgressBar()
        self.progress.setVisible(False)
        self.progress.setRange(0, 100)
        ly.addWidget(self.progress)

        # ── Log output ──
        self.log = QTextEdit()
        self.log.setVisible(False)
        self.log.setMaximumHeight(130)
        self.log.setReadOnly(True)
        self.log.setStyleSheet(
            "QTextEdit { font-family: Consolas, monospace; font-size: 11px; "
            "background-color: #1e1e1e; color: #d4d4d4; }"
        )
        ly.addWidget(self.log)

    # ───────────────────────────────────────────────────────────
    # Plugin pattern required method
    # ───────────────────────────────────────────────────────────

    def refresh_layers(self):
        """Required by the LVT plugin pattern — no-op."""

    # ───────────────────────────────────────────────────────────
    # Browse
    # ───────────────────────────────────────────────────────────

    def _browse_dir(self):
        """Open folder picker."""
        folder = QFileDialog.getExistingDirectory(
            self, "Chọn thư mục cần dọn dẹp",
            self.txt_dir.text() or "")
        if folder:
            self.txt_dir.setText(folder)

    # ───────────────────────────────────────────────────────────
    # Scan
    # ───────────────────────────────────────────────────────────

    def _scan(self):
        """Scan for matching layers and files."""
        keywords_raw = self.txt_keywords.text().strip()
        if not keywords_raw:
            QMessageBox.warning(self, 'LVT', 'Vui lòng nhập từ khóa.')
            return

        keywords = [k.strip().lower() for k in keywords_raw.split(',')
                    if k.strip()]
        if not keywords:
            QMessageBox.warning(self, 'LVT', 'Từ khóa không hợp lệ.')
            return

        # Parse extension filter
        ext_raw = self.txt_extensions.text().strip()
        ext_filter = set()
        if ext_raw:
            for e in ext_raw.split(','):
                e = e.strip().lower()
                if e and not e.startswith('.'):
                    e = '.' + e
                if e:
                    ext_filter.add(e)

        # Reset results
        self._scan_results.clear()
        self._matched_layers.clear()
        self.tree_files.clear()
        self.tree_layers.clear()

        # ── Step 1: Scan QGIS layers ──
        if self.chk_layers.isChecked():
            project = QgsProject.instance()
            for layer_id, layer in project.mapLayers().items():
                name_lower = layer.name().lower()
                source_lower = layer.source().lower()
                for kw in keywords:
                    if kw in name_lower or kw in source_lower:
                        self._matched_layers.append(
                            (layer_id, layer.name(), layer.source()))
                        break

        # ── Step 2: Scan disk files ──
        scan_dir = self.txt_dir.text().strip()
        if self.chk_disk.isChecked() and scan_dir and os.path.isdir(scan_dir):
            if self.chk_subfolders.isChecked():
                for root, dirs, files in os.walk(scan_dir):
                    for f in files:
                        self._check_file(
                            os.path.join(root, f), f, keywords, ext_filter)
            else:
                for f in os.listdir(scan_dir):
                    full = os.path.join(scan_dir, f)
                    if os.path.isfile(full):
                        self._check_file(full, f, keywords, ext_filter)

        # ── Build UI results ──
        self._show_results(keywords)

    def _check_file(self, filepath, filename, keywords, ext_filter):
        """Check if a file matches keywords and extension filter."""
        name_lower = filename.lower()

        # Extension filter
        if ext_filter:
            _, ext = os.path.splitext(name_lower)
            if ext not in ext_filter:
                return

        # Keyword match
        for kw in keywords:
            if kw in name_lower:
                try:
                    size = os.path.getsize(filepath)
                except OSError:
                    size = 0
                self._scan_results.append((filepath, size, kw))
                return

    def _show_results(self, keywords):
        """Populate result trees and summary."""
        total_files = len(self._scan_results)
        total_layers = len(self._matched_layers)
        total_size = sum(s for _, s, _ in self._scan_results)

        # Summary
        parts = []
        if total_layers > 0:
            parts.append(f"📌 {total_layers} layer QGIS")
        if total_files > 0:
            parts.append(f"📁 {total_files} file ({_format_size(total_size)})")
        if not parts:
            self.lbl_summary.setText(
                "✅ Không tìm thấy file/layer nào khớp từ khóa.")
            self.lbl_summary.setStyleSheet("color: #2e7d32; font-weight: bold;")
            self._hide_result_widgets()
            return

        self.lbl_summary.setText(
            f"⚠️ Tìm thấy: {' • '.join(parts)} — khớp từ khóa: "
            f"{', '.join(keywords)}")
        self.lbl_summary.setStyleSheet("color: #e65100; font-weight: bold;")

        # Layers tree
        if total_layers > 0:
            self.lbl_layers.setVisible(True)
            self.tree_layers.setVisible(True)
            for lid, lname, lsource in self._matched_layers:
                item = QTreeWidgetItem(self.tree_layers)
                item.setFlags(item.flags() | QtCompat.ItemIsUserCheckable)
                item.setCheckState(0, QtCompat.Checked)
                item.setText(0, lname)
                item.setText(1, os.path.basename(lsource))
                item.setData(0, QtCompat.UserRole, lid)
        else:
            self.lbl_layers.setVisible(False)
            self.tree_layers.setVisible(False)

        # Files tree
        if total_files > 0:
            self.lbl_files.setVisible(True)
            self.tree_files.setVisible(True)
            self.btn_select_all.setVisible(True)
            self.btn_deselect_all.setVisible(True)

            for fpath, fsize, kw in sorted(
                    self._scan_results, key=lambda x: x[0]):
                item = QTreeWidgetItem(self.tree_files)
                item.setFlags(item.flags() | QtCompat.ItemIsUserCheckable)
                item.setCheckState(0, QtCompat.Checked)
                item.setText(0, os.path.basename(fpath))
                item.setText(1, _format_size(fsize))
                item.setText(2, kw)
                item.setText(3, os.path.dirname(fpath))
                item.setData(0, QtCompat.UserRole, fpath)
        else:
            self.lbl_files.setVisible(False)
            self.tree_files.setVisible(False)
            self.btn_select_all.setVisible(False)
            self.btn_deselect_all.setVisible(False)

        # Show delete button
        self.btn_delete.setVisible(total_files > 0 or total_layers > 0)

    def _hide_result_widgets(self):
        """Hide all result widgets."""
        self.lbl_layers.setVisible(False)
        self.tree_layers.setVisible(False)
        self.lbl_files.setVisible(False)
        self.tree_files.setVisible(False)
        self.btn_select_all.setVisible(False)
        self.btn_deselect_all.setVisible(False)
        self.btn_delete.setVisible(False)

    def _select_all(self):
        """Check all file items."""
        for i in range(self.tree_files.topLevelItemCount()):
            self.tree_files.topLevelItem(i).setCheckState(0, QtCompat.Checked)

    def _deselect_all(self):
        """Uncheck all file items."""
        for i in range(self.tree_files.topLevelItemCount()):
            self.tree_files.topLevelItem(i).setCheckState(0, QtCompat.Unchecked)

    # ───────────────────────────────────────────────────────────
    # Delete (with confirmation)
    # ───────────────────────────────────────────────────────────

    def _delete(self):
        """Delete selected files and remove selected layers — with confirmation."""
        # Count checked items
        layers_to_remove = []
        for i in range(self.tree_layers.topLevelItemCount()):
            item = self.tree_layers.topLevelItem(i)
            if item.checkState(0) == QtCompat.Checked:
                layers_to_remove.append(
                    (item.data(0, QtCompat.UserRole), item.text(0)))

        files_to_delete = []
        total_size = 0
        for i in range(self.tree_files.topLevelItemCount()):
            item = self.tree_files.topLevelItem(i)
            if item.checkState(0) == QtCompat.Checked:
                fpath = item.data(0, QtCompat.UserRole)
                files_to_delete.append(fpath)
                try:
                    total_size += os.path.getsize(fpath)
                except OSError:
                    pass  # nosec B110

        if not layers_to_remove and not files_to_delete:
            QMessageBox.information(
                self, 'LVT', 'Không có mục nào được chọn để xóa.')
            return

        # ── Confirmation dialog ──
        msg_parts = []
        if layers_to_remove:
            msg_parts.append(
                f"📌 Gỡ {len(layers_to_remove)} layer khỏi QGIS:")
            for _, name in layers_to_remove[:5]:
                msg_parts.append(f"   • {name}")
            if len(layers_to_remove) > 5:
                msg_parts.append(
                    f"   ... và {len(layers_to_remove) - 5} layer khác")

        if files_to_delete:
            msg_parts.append(
                f"\n📁 Xóa {len(files_to_delete)} file "
                f"({_format_size(total_size)}) trên ổ cứng:")
            for f in files_to_delete[:8]:
                msg_parts.append(f"   • {os.path.basename(f)}")
            if len(files_to_delete) > 8:
                msg_parts.append(
                    f"   ... và {len(files_to_delete) - 8} file khác")

        msg_parts.append("\n⚠️ HÀNH ĐỘNG NÀY KHÔNG THỂ HOÀN TÁC!")
        msg_parts.append("Bạn có chắc chắn muốn tiếp tục?")

        reply = QMessageBox.warning(
            self, '⚠️ Xác nhận xóa',
            '\n'.join(msg_parts),
            MsgButton.Yes | MsgButton.No,
            MsgButton.No)

        if reply != MsgButton.Yes:
            return

        # ── Second confirmation for large deletions ──
        if len(files_to_delete) > 20 or total_size > 100 * 1024 * 1024:
            reply2 = QMessageBox.critical(
                self, '🚨 Cảnh báo quan trọng',
                f"Bạn sắp xóa {len(files_to_delete)} file "
                f"({_format_size(total_size)}).\n\n"
                f"Đây là số lượng LỚN. Xác nhận lần cuối?",
                MsgButton.Yes | MsgButton.No,
                MsgButton.No)
            if reply2 != MsgButton.Yes:
                return

        # ── Execute deletion ──
        self.log.setVisible(True)
        self.log.clear()
        self.progress.setVisible(True)
        self.btn_delete.setEnabled(False)
        QApplication.processEvents()

        # Step 1: Remove QGIS layers
        if layers_to_remove:
            self.log.append("=" * 50)
            self.log.append("📌 GỠ LAYER KHỎI QGIS")
            layer_ids = [lid for lid, _ in layers_to_remove]
            QgsProject.instance().removeMapLayers(layer_ids)
            for _, name in layers_to_remove:
                self.log.append(f"  ✅ Đã gỡ: {name}")
            self.log.append(
                f"→ Gỡ thành công {len(layers_to_remove)} layer")
            QApplication.processEvents()

        # Step 2: Delete files
        if files_to_delete:
            self.log.append("\n" + "=" * 50)
            self.log.append("📁 XÓA FILE TRÊN Ổ CỨNG")
            deleted = 0
            errors = 0
            total = len(files_to_delete)

            for idx, fpath in enumerate(files_to_delete):
                self.progress.setValue(int((idx / total) * 100))
                try:
                    os.remove(fpath)
                    self.log.append(f"  ✅ {os.path.basename(fpath)}")
                    deleted += 1
                except PermissionError:
                    self.log.append(
                        f"  ❌ {os.path.basename(fpath)} — "
                        f"File đang bị khóa (đang mở?)")
                    errors += 1
                except Exception as e:
                    self.log.append(
                        f"  ❌ {os.path.basename(fpath)} — {e}")
                    errors += 1

                if idx % 10 == 0:
                    QApplication.processEvents()

            self.log.append(
                f"\n→ Đã xóa {deleted}/{total} file"
                f" ({_format_size(total_size)})")
            if errors:
                self.log.append(f"⚠️ {errors} file không xóa được")

        # Done
        self.progress.setValue(100)
        self.btn_delete.setEnabled(True)
        self.log.append(f"\n{'=' * 50}")
        self.log.append("🎉 HOÀN TẤT DỌN DẸP!")

        # Refresh scan results
        QTimer.singleShot(500, self._scan)

        QMessageBox.information(
            self, '✅ Hoàn tất',
            f'Đã dọn dẹp xong!\n'
            f'• {len(layers_to_remove)} layer gỡ khỏi QGIS\n'
            f'• {deleted if files_to_delete else 0} file đã xóa')
