# -*- coding: utf-8 -*-
"""
LVT4U CAD → SHP Converter — Convert DWG/DXF/DGN files to Shapefile.

Core flow:
    1. Select DWG/DXF/DGN file
    2. Set coordinate system (VN-2000 / Custom / WGS84)
    3. Scan file to discover CAD layers
    4. Select layers to convert
    5. Set output folder and options
    6. Convert selected layers to SHP

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

import os
import tempfile

from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import (
    QDialog, QVBoxLayout, QHBoxLayout, QLabel,
    QComboBox, QPushButton, QFileDialog, QMessageBox, QGroupBox,
    QProgressBar, QApplication, QCheckBox, QLineEdit,
    QRadioButton, QTreeWidget, QTreeWidgetItem, QHeaderView,
)
from qgis.core import (
    QgsProject, QgsVectorLayer, QgsVectorFileWriter,
    QgsCoordinateReferenceSystem, QgsCoordinateTransform,
    QgsCoordinateTransformContext,
)
from qgis.gui import QgsProjectionSelectionWidget

from ..shared.i18n import current_language
from ..shared.help_dialog import HelpDialog

# ── Bilingual labels ──────────────────────────────────────────────────
_VI = {
    'DWG/DXF/DGN → SHP Converter': 'Chuyển đổi CAD → SHP',
    'Source File': 'File nguồn',
    'Browse...': 'Chọn file...',
    'Supported: .dwg .dxf .dgn': 'Hỗ trợ: .dwg .dxf .dgn',
    'Coordinate System': 'Hệ tọa độ',
    'VN-2000': 'VN-2000',
    'Custom CRS': 'Tùy chỉnh',
    'Undefined (WGS84)': 'Không xác định (WGS84)',
    'Scan File': 'Quét file',
    'CAD Layers': 'Danh sách lớp CAD',
    'Select All': 'Chọn tất cả',
    'Deselect All': 'Bỏ tất cả',
    'Layer Name': 'Tên lớp',
    'Type': 'Kiểu',
    'Features': 'Số ĐT',
    'Options': 'Tùy chọn',
    'Expand block references': 'Phân tách Block thành đối tượng đơn',
    'Add to map after conversion': 'Thêm vào bản đồ sau khi chuyển đổi',
    'Separate SHP per geometry type': 'Tách SHP theo kiểu hình học',
    'Output Folder': 'Thư mục xuất',
    'Browse Folder...': 'Chọn thư mục...',
    'Convert': 'Chuyển đổi',
    'Close': 'Đóng',
    'Scanning file...': 'Đang quét file...',
    'Converting...': 'Đang chuyển đổi...',
    'No layers selected': 'Chưa chọn lớp nào',
    'Select source file first': 'Chọn file nguồn trước',
    'Select output folder': 'Chọn thư mục xuất',
    'Conversion complete': 'Chuyển đổi hoàn tất',
    'layers converted': 'lớp đã chuyển đổi',
    'features total': 'đối tượng',
    'No file selected': 'Chưa chọn file',
    'Error': 'Lỗi',
    'Processing...': 'Đang xử lý...',
    'No layers found': 'Không tìm thấy lớp nào trong file',
    'Guide': 'Hướng dẫn',
}

# Geometry type mapping for display
_GEOM_MAP = {
    'Point': 'Point', 'LineString': 'Line', 'Polygon': 'Polygon',
    'MultiPoint': 'Point', 'MultiLineString': 'Line',
    'MultiPolygon': 'Polygon', 'CompoundCurve': 'Line',
    'CircularString': 'Line', 'CurvePolygon': 'Polygon',
    'Unknown': 'Unknown', 'No Geometry': 'None',
    'GeometryCollection': 'Mixed',
}


class DwgConverterDialog(QDialog):
    """Dialog for converting DWG/DXF/DGN files to ESRI Shapefile."""

    def __init__(self, iface, parent=None):
        super().__init__(parent or iface.mainWindow())
        self.iface = iface
        self.lang = current_language()
        self._temp_gpkg = None

        self.setMinimumSize(800, 650)
        self.setWindowFlags(
            self.windowFlags() & ~Qt.WindowContextHelpButtonHint
        )

        self._setup_ui()
        self._refresh_ui_text()

    # ------------------------------------------------------------------
    # Translation helper
    # ------------------------------------------------------------------

    def _t(self, text):
        """Translate text based on current language."""
        return _VI.get(text, text) if self.lang == 'vi' else text

    # ------------------------------------------------------------------
    # UI Construction
    # ------------------------------------------------------------------

    def _setup_ui(self):
        """Build the dialog UI."""
        main_ly = QVBoxLayout(self)
        main_ly.setSpacing(8)

        # ── Language toggle (top-right) ──
        top_row = QHBoxLayout()
        top_row.addStretch()
        self.btn_lang = QPushButton("🇬🇧 English")
        self.btn_lang.setFixedWidth(120)
        self.btn_lang.clicked.connect(self._toggle_lang)
        top_row.addWidget(self.btn_lang)
        main_ly.addLayout(top_row)

        # ── Source file ──
        src_row = QHBoxLayout()
        self.lbl_source = QLabel("📁 Source File:")
        self.txt_input = QLineEdit()
        self.txt_input.setReadOnly(True)
        self.txt_input.setPlaceholderText("...")
        self.btn_browse_input = QPushButton("Browse...")
        self.btn_browse_input.clicked.connect(self._browse_input)
        src_row.addWidget(self.lbl_source)
        src_row.addWidget(self.txt_input, 1)
        src_row.addWidget(self.btn_browse_input)
        main_ly.addLayout(src_row)

        self.lbl_support = QLabel("Supported: .dwg .dxf .dgn")
        self.lbl_support.setStyleSheet("color: #888; font-size: 11px;")
        main_ly.addWidget(self.lbl_support)

        # ── CRS Group ──
        self.grp_crs = self._build_crs_group()
        main_ly.addWidget(self.grp_crs)

        # ── Scan button ──
        scan_row = QHBoxLayout()
        self.btn_scan = QPushButton("🔍 Scan File")
        self.btn_scan.setFixedHeight(34)
        self.btn_scan.setStyleSheet(
            "QPushButton{background:#1565c0;color:#fff;font-weight:bold;"
            "padding:4px 14px;border-radius:4px}"
            "QPushButton:hover{background:#1976d2}"
        )
        self.btn_scan.clicked.connect(self._scan_file)
        scan_row.addWidget(self.btn_scan)
        scan_row.addStretch()
        main_ly.addLayout(scan_row)

        # ── CAD Layers ──
        self.grp_layers = QGroupBox("CAD Layers")
        layers_ly = QVBoxLayout(self.grp_layers)

        # Select/Deselect buttons
        sel_row = QHBoxLayout()
        self.btn_select_all = QPushButton("Select All")
        self.btn_select_all.setFixedHeight(28)
        self.btn_select_all.clicked.connect(self._select_all)
        self.btn_deselect_all = QPushButton("Deselect All")
        self.btn_deselect_all.setFixedHeight(28)
        self.btn_deselect_all.clicked.connect(self._deselect_all)
        sel_row.addWidget(self.btn_select_all)
        sel_row.addWidget(self.btn_deselect_all)
        sel_row.addStretch()
        self.lbl_summary = QLabel("")
        self.lbl_summary.setStyleSheet("color: #555; font-style: italic;")
        sel_row.addWidget(self.lbl_summary)
        layers_ly.addLayout(sel_row)

        # Tree widget for layer list
        self.tree_layers = QTreeWidget()
        self.tree_layers.setHeaderLabels(["Layer Name", "Type", "Features"])
        self.tree_layers.setRootIsDecorated(False)
        self.tree_layers.setAlternatingRowColors(True)
        header = self.tree_layers.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.tree_layers.setMinimumHeight(150)
        self.tree_layers.itemChanged.connect(
            lambda: self._update_layer_summary())
        layers_ly.addWidget(self.tree_layers)
        main_ly.addWidget(self.grp_layers)

        # ── Options ──
        self.grp_options = QGroupBox("Options")
        opt_ly = QVBoxLayout(self.grp_options)
        self.chk_expand_blocks = QCheckBox("Expand block references")
        self.chk_expand_blocks.setChecked(True)
        self.chk_add_to_map = QCheckBox("Add to map after conversion")
        self.chk_add_to_map.setChecked(True)
        self.chk_separate_geom = QCheckBox(
            "Separate SHP per geometry type")
        self.chk_separate_geom.setChecked(True)
        opt_ly.addWidget(self.chk_expand_blocks)
        opt_ly.addWidget(self.chk_add_to_map)
        opt_ly.addWidget(self.chk_separate_geom)
        main_ly.addWidget(self.grp_options)

        # ── Output folder ──
        out_row = QHBoxLayout()
        self.lbl_output = QLabel("📂 Output Folder:")
        self.txt_output = QLineEdit()
        self.txt_output.setReadOnly(True)
        self.txt_output.setPlaceholderText("...")
        self.btn_browse_output = QPushButton("Browse Folder...")
        self.btn_browse_output.clicked.connect(self._browse_output)
        out_row.addWidget(self.lbl_output)
        out_row.addWidget(self.txt_output, 1)
        out_row.addWidget(self.btn_browse_output)
        main_ly.addLayout(out_row)

        # ── Progress bar ──
        self.progress = QProgressBar()
        self.progress.setVisible(False)
        self.progress.setTextVisible(True)
        main_ly.addWidget(self.progress)

        # ── Status label ──
        self.lbl_status = QLabel("")
        self.lbl_status.setStyleSheet("color: #555;")
        main_ly.addWidget(self.lbl_status)

        # ── Bottom buttons ──
        btn_row = QHBoxLayout()
        self.btn_help = QPushButton("❓ Guide")
        self.btn_help.setFixedHeight(36)
        self.btn_help.setStyleSheet(
            "QPushButton{background:#ff8f00;color:#fff;font-weight:bold;"
            "padding:4px 14px;border-radius:4px}"
            "QPushButton:hover{background:#ffa000}"
        )
        self.btn_help.clicked.connect(self._show_help)
        btn_row.addWidget(self.btn_help)

        btn_row.addStretch()

        self.btn_convert = QPushButton("🔄 Convert")
        self.btn_convert.setFixedHeight(36)
        self.btn_convert.setStyleSheet(
            "QPushButton{background:#2e7d32;color:#fff;font-weight:bold;"
            "padding:4px 20px;border-radius:4px}"
            "QPushButton:hover{background:#388e3c}"
        )
        self.btn_convert.clicked.connect(self._convert)
        btn_row.addWidget(self.btn_convert)

        self.btn_close = QPushButton("Close")
        self.btn_close.setFixedHeight(36)
        self.btn_close.clicked.connect(self.close)
        btn_row.addWidget(self.btn_close)

        main_ly.addLayout(btn_row)

    # ------------------------------------------------------------------
    # CRS Group Builder
    # ------------------------------------------------------------------

    def _build_crs_group(self):
        """Build CRS selection UI with VN-2000 zones."""
        group = QGroupBox("Coordinate System")
        layout = QVBoxLayout(group)

        # Radio buttons
        self.radio_vn2000 = QRadioButton("VN-2000")
        self.radio_vn2000.setChecked(True)
        self.radio_custom = QRadioButton("Custom CRS")
        self.radio_wgs84 = QRadioButton("Undefined (WGS84)")

        # VN-2000 zone combo — load from root _crs_list
        from .._crs_list import CRS_LIST
        self.cmb_vn2000 = QComboBox()
        for label, epsg in CRS_LIST:
            if not epsg:
                # Section header — add as disabled separator
                self.cmb_vn2000.addItem(f"── {label} ──", "")
                idx = self.cmb_vn2000.count() - 1
                model = self.cmb_vn2000.model()
                item = model.item(idx)
                if item:
                    item.setEnabled(False)
            else:
                self.cmb_vn2000.addItem(label, epsg)
        # Default to first valid entry
        for i in range(self.cmb_vn2000.count()):
            if self.cmb_vn2000.itemData(i):
                self.cmb_vn2000.setCurrentIndex(i)
                break

        # Custom CRS widget
        self.crs_widget = QgsProjectionSelectionWidget()
        self.crs_widget.setCrs(
            QgsCoordinateReferenceSystem("EPSG:4326"))
        self.crs_widget.setEnabled(False)

        # Row 1: VN-2000 radio + combo
        h1 = QHBoxLayout()
        h1.addWidget(self.radio_vn2000)
        h1.addWidget(self.cmb_vn2000, 1)
        layout.addLayout(h1)

        # Row 2: Custom radio + CRS widget
        h2 = QHBoxLayout()
        h2.addWidget(self.radio_custom)
        h2.addWidget(self.crs_widget, 1)
        layout.addLayout(h2)

        # Row 3: WGS84 radio
        layout.addWidget(self.radio_wgs84)

        # Signals — enable/disable associated widgets
        self.radio_vn2000.toggled.connect(
            lambda on: self.cmb_vn2000.setEnabled(on))
        self.radio_custom.toggled.connect(
            lambda on: self.crs_widget.setEnabled(on))

        return group

    # ------------------------------------------------------------------
    # CRS Selection
    # ------------------------------------------------------------------

    def _get_selected_crs(self):
        """Get the currently selected CRS."""
        if self.radio_vn2000.isChecked():
            epsg = self.cmb_vn2000.currentData()
            if epsg:
                return QgsCoordinateReferenceSystem(epsg)
            return QgsCoordinateReferenceSystem("EPSG:4326")
        elif self.radio_custom.isChecked():
            return self.crs_widget.crs()
        else:
            return QgsCoordinateReferenceSystem("EPSG:4326")

    # ------------------------------------------------------------------
    # Language
    # ------------------------------------------------------------------

    def _toggle_lang(self):
        """Toggle between Vietnamese and English."""
        self.lang = 'en' if self.lang == 'vi' else 'vi'
        self._refresh_ui_text()

    def _refresh_ui_text(self):
        """Update all widget texts for current language."""
        t = self._t
        self.setWindowTitle(f"LVT4U — {t('DWG/DXF/DGN → SHP Converter')}")
        self.btn_lang.setText(
            "🇬🇧 English" if self.lang == 'en' else "🇻🇳 Tiếng Việt"
        )

        # Source file
        self.lbl_source.setText(f"📁 {t('Source File')}:")
        self.btn_browse_input.setText(t('Browse...'))
        self.lbl_support.setText(t('Supported: .dwg .dxf .dgn'))

        # CRS group
        self.grp_crs.setTitle(t('Coordinate System'))
        self.radio_vn2000.setText(t('VN-2000'))
        self.radio_custom.setText(t('Custom CRS'))
        self.radio_wgs84.setText(t('Undefined (WGS84)'))

        # Scan button
        self.btn_scan.setText(f"🔍 {t('Scan File')}")

        # Layers group
        self.grp_layers.setTitle(t('CAD Layers'))
        self.btn_select_all.setText(t('Select All'))
        self.btn_deselect_all.setText(t('Deselect All'))
        self.tree_layers.setHeaderLabels([
            t('Layer Name'), t('Type'), t('Features')
        ])

        # Options group
        self.grp_options.setTitle(t('Options'))
        self.chk_expand_blocks.setText(t('Expand block references'))
        self.chk_add_to_map.setText(t('Add to map after conversion'))
        self.chk_separate_geom.setText(
            t('Separate SHP per geometry type'))

        # Output folder
        self.lbl_output.setText(f"📂 {t('Output Folder')}:")
        self.btn_browse_output.setText(t('Browse Folder...'))

        # Bottom buttons
        self.btn_convert.setText(f"🔄 {t('Convert')}")
        self.btn_close.setText(t('Close'))
        self.btn_help.setText(f"❓ {t('Guide')}")

    # ------------------------------------------------------------------
    # File Browsing
    # ------------------------------------------------------------------

    def _browse_input(self):
        """Browse for DWG/DXF file."""
        path, _ = QFileDialog.getOpenFileName(
            self, 'Select CAD File', '',
            'CAD Files (*.dwg *.dxf *.dgn *.gpkg);;DWG (*.dwg);;DXF (*.dxf);;DGN (*.dgn);;GeoPackage (*.gpkg)'
        )
        if path:
            self.txt_input.setText(path)
            # Auto-set output folder to same directory
            if not self.txt_output.text().strip():
                self.txt_output.setText(os.path.dirname(path))

    def _browse_output(self):
        """Browse for output folder."""
        folder = QFileDialog.getExistingDirectory(
            self, 'Select Output Folder')
        if folder:
            self.txt_output.setText(folder)

    def _open_qgis_dwg_import(self):
        """Open QGIS's built-in DWG/DXF Import dialog."""
        try:
            # Find the Import DWG/DXF action in QGIS menus
            main_window = self.iface.mainWindow()
            for action in main_window.findChildren(
                    type(main_window.menuBar().actions()[0])):
                if 'dwg' in action.text().lower() or \
                   'dxf' in action.text().lower():
                    action.trigger()
                    return

            # Fallback: try the Project menu directly
            project_menu = self.iface.projectMenu()
            if project_menu:
                for action in project_menu.actions():
                    txt = action.text().lower()
                    if 'import' in txt and ('dwg' in txt or 'dxf' in txt):
                        action.trigger()
                        return
                    # Check submenus
                    if action.menu():
                        for sub_action in action.menu().actions():
                            txt2 = sub_action.text().lower()
                            if 'dwg' in txt2 or 'dxf' in txt2:
                                sub_action.trigger()
                                return

            # Last resort: try Import/Export submenu
            for action in main_window.menuBar().actions():
                if action.menu():
                    for sub in action.menu().actions():
                        if sub.menu():
                            for sub2 in sub.menu().actions():
                                txt = sub2.text().lower()
                                if 'dwg' in txt or 'dxf' in txt:
                                    sub2.trigger()
                                    return

            QMessageBox.information(
                self, 'LVT4U',
                'Vào menu: Project → Import/Export → '
                'Import Layers from DWG/DXF'
                if self.lang == 'vi' else
                'Go to: Project → Import/Export → '
                'Import Layers from DWG/DXF')
        except Exception:
            QMessageBox.information(
                self, 'LVT4U',
                'Vào menu: Project → Import/Export → '
                'Import Layers from DWG/DXF'
                if self.lang == 'vi' else
                'Go to: Project → Import/Export → '
                'Import Layers from DWG/DXF')

    # ------------------------------------------------------------------
    # Layer Selection
    # ------------------------------------------------------------------

    def _select_all(self):
        """Check all layers in the tree widget."""
        for i in range(self.tree_layers.topLevelItemCount()):
            self.tree_layers.topLevelItem(i).setCheckState(0, Qt.Checked)
        self._update_layer_summary()

    def _deselect_all(self):
        """Uncheck all layers in the tree widget."""
        for i in range(self.tree_layers.topLevelItemCount()):
            self.tree_layers.topLevelItem(i).setCheckState(0, Qt.Unchecked)
        self._update_layer_summary()

    def _update_layer_summary(self):
        """Update the summary label with selected/total count."""
        total = self.tree_layers.topLevelItemCount()
        checked = 0
        total_features = 0
        for i in range(total):
            item = self.tree_layers.topLevelItem(i)
            if item.checkState(0) == Qt.Checked:
                checked += 1
                data = item.data(0, Qt.UserRole)
                if data:
                    total_features += data.get('count', 0)
        self.lbl_summary.setText(
            f"{checked}/{total} — {total_features} {self._t('features total')}"
        )

    # ------------------------------------------------------------------
    # Scan File
    # ------------------------------------------------------------------

    def _scan_file(self):
        """Scan DWG/DXF and populate layer list.

        Strategy for DXF:
            Direct OGR loading → sublayers → populate tree

        Strategy for DWG:
            1. Try direct OGR (works for DWG R2000 only)
            2. Try QGIS internal DWG import → GeoPackage → read layers
            3. Guide user to use QGIS Import DWG dialog first
        """
        input_path = self.txt_input.text().strip()
        if not input_path or not os.path.exists(input_path):
            QMessageBox.warning(
                self, 'LVT4U', self._t('No file selected'))
            return

        self.progress.setVisible(True)
        self.progress.setValue(10)
        self.progress.setFormat(self._t('Scanning file...'))
        self.lbl_status.setText(self._t('Scanning file...'))
        QApplication.processEvents()

        try:
            crs = self._get_selected_crs()
            is_dwg = input_path.lower().endswith('.dwg')
            is_dxf = input_path.lower().endswith('.dxf')

            # Prepare temp GeoPackage path
            self._temp_gpkg = os.path.join(
                tempfile.gettempdir(), 'lvt_dwg_import.gpkg')
            for ext in ['', '-wal', '-shm']:
                p = self._temp_gpkg + ext
                if os.path.exists(p):
                    try:
                        os.remove(p)
                    except OSError:
                        pass

            scan_ok = False
            is_gpkg = input_path.lower().endswith('.gpkg')
            is_dgn = input_path.lower().endswith('.dgn')

            if is_gpkg:
                # ── GPKG: Direct loading (from previous QGIS DWG import) ──
                self._temp_gpkg = input_path
                self._populate_layer_list_from_gpkg(input_path)
                scan_ok = self.tree_layers.topLevelItemCount() > 0
            elif is_dxf or is_dgn:
                # ── DXF/DGN: Direct OGR loading (always works) ──
                scan_ok = self._scan_dxf_direct(input_path)
            elif is_dwg:
                # ── DWG: Multi-method approach ──
                scan_ok = self._scan_dwg(input_path, crs)

            if not scan_ok:
                self._show_dwg_help_dialog(input_path, is_dwg)

        except Exception as e:
            QMessageBox.critical(
                self, 'LVT4U', f'{self._t("Error")}: {str(e)}')
        finally:
            self.progress.setVisible(False)
            self.lbl_status.setText("")

    def _scan_dxf_direct(self, dxf_path):
        """Scan DXF file directly via OGR — always works."""
        self.progress.setFormat(self._t('Scanning file...') + ' (DXF)')
        QApplication.processEvents()

        vlayer = QgsVectorLayer(dxf_path, 'scan', 'ogr')
        if not vlayer.isValid():
            vlayer = QgsVectorLayer(
                f'{dxf_path}|option:DXF_ENCODING=UTF-8',
                'scan', 'ogr')
        if not vlayer.isValid():
            return False

        sub_layers = vlayer.dataProvider().subLayers()
        if not sub_layers:
            del vlayer
            return False

        self.tree_layers.clear()
        for sub in sub_layers:
            parts = sub.split('!!::!!')
            if len(parts) < 4:
                continue
            layer_name = parts[1]
            count = parts[2]
            geom_type = parts[3]
            display_geom = _GEOM_MAP.get(geom_type, geom_type)

            item = QTreeWidgetItem()
            item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
            item.setCheckState(
                0, Qt.Checked if int(count) > 0 else Qt.Unchecked)
            item.setText(0, layer_name)
            item.setText(1, display_geom)
            item.setText(2, str(count))
            item.setData(0, Qt.UserRole, {
                'name': layer_name,
                'geom_type': geom_type,
                'count': int(count),
                'source': 'ogr',
                'input_path': dxf_path,
            })
            self.tree_layers.addTopLevelItem(item)

        del vlayer
        self._update_layer_summary()
        self.progress.setValue(100)
        QApplication.processEvents()

        if self.tree_layers.topLevelItemCount() == 0:
            return False
        return True

    def _scan_dwg(self, dwg_path, crs):
        """Scan DWG file using multiple strategies."""
        errors = []

        # --- Method 1: Try QGIS DWG Importer (C++ internal) ---
        self.progress.setFormat(
            self._t('Scanning file...') + ' (QGIS Importer)')
        self.progress.setValue(20)
        QApplication.processEvents()

        try:
            ok = self._try_qgis_dwg_importer(dwg_path, crs)
            if ok:
                return True
        except Exception as e:
            errors.append(f'QgsDwgImporter: {e}')

        # --- Method 2: Try Processing algorithms ---
        self.progress.setFormat(
            self._t('Scanning file...') + ' (Processing)')
        self.progress.setValue(40)
        QApplication.processEvents()

        try:
            ok = self._try_processing_import(dwg_path, crs)
            if ok:
                return True
        except Exception as e:
            errors.append(f'Processing: {e}')

        # --- Method 3: Try direct OGR (only DWG R2000) ---
        self.progress.setFormat(
            self._t('Scanning file...') + ' (OGR/CAD)')
        self.progress.setValue(60)
        QApplication.processEvents()

        try:
            ok = self._scan_dxf_direct(dwg_path)  # OGR can try CAD driver
            if ok:
                return True
        except Exception as e:
            errors.append(f'OGR: {e}')

        # --- Method 4: Try loading from existing GeoPackage ---
        # (user may have already imported via QGIS menu)
        self.progress.setFormat(
            self._t('Scanning file...') + ' (GPKG)')
        self.progress.setValue(80)
        QApplication.processEvents()

        try:
            ok = self._try_load_existing_gpkg(dwg_path)
            if ok:
                return True
        except Exception as e:
            errors.append(f'GPKG: {e}')

        self._last_errors = errors
        return False

    def _try_qgis_dwg_importer(self, dwg_path, crs):
        """Try QGIS's internal QgsDwgImporter (available in some builds)."""
        # Try to import QgsDwgImporter from various locations
        QgsDwgImporter = None
        for module_path in [
            'qgis.core', 'qgis._core', 'qgis.app', 'qgis._app',
        ]:
            try:
                mod = __import__(module_path, fromlist=['QgsDwgImporter'])
                QgsDwgImporter = getattr(mod, 'QgsDwgImporter', None)
                if QgsDwgImporter:
                    break
            except (ImportError, AttributeError):
                continue

        if QgsDwgImporter is None:
            raise ImportError('QgsDwgImporter not available in Python bindings')

        crs_obj = crs if crs and crs.isValid() else \
            QgsCoordinateReferenceSystem('EPSG:4326')

        importer = QgsDwgImporter(self._temp_gpkg, crs_obj)
        expand = self.chk_expand_blocks.isChecked()

        if hasattr(importer, 'import_'):
            result = importer.import_(dwg_path, expand)
        elif hasattr(importer, 'importFile'):
            result = importer.importFile(dwg_path, expand)
        else:
            raise AttributeError('No import method found on QgsDwgImporter')

        if not result or not os.path.exists(self._temp_gpkg):
            raise RuntimeError('QgsDwgImporter returned error')

        self._populate_layer_list_from_gpkg(self._temp_gpkg)
        return self.tree_layers.topLevelItemCount() > 0

    def _try_processing_import(self, input_path, crs):
        """Try Processing algorithms for DWG/DXF import."""
        import processing
        from qgis.core import QgsApplication

        crs_str = crs.authid() if crs and crs.isValid() else 'EPSG:4326'

        alg_names = [
            'native:importdwglayers',
            'native:importdxflayers',
            'qgis:importdwglayers',
            'qgis:importdxflayers',
        ]

        for alg_name in alg_names:
            alg = QgsApplication.processingRegistry().algorithmById(alg_name)
            if alg is None:
                continue

            params = {
                'INPUT': input_path,
                'TARGET_PACKAGE': self._temp_gpkg,
            }
            param_names = [p.name() for p in alg.parameterDefinitions()]

            if 'CRS' in param_names:
                params['CRS'] = crs_str
            if 'CRS_OUTPUT' in param_names:
                params['CRS_OUTPUT'] = crs_str
            if 'EXPAND_BLOCKS' in param_names:
                params['EXPAND_BLOCKS'] = self.chk_expand_blocks.isChecked()

            try:
                processing.run(alg_name, params)
                if os.path.exists(self._temp_gpkg):
                    self._populate_layer_list_from_gpkg(self._temp_gpkg)
                    if self.tree_layers.topLevelItemCount() > 0:
                        return True
            except Exception:
                continue

        return False

    def _try_load_existing_gpkg(self, dwg_path):
        """Check if there's an existing GeoPackage from previous DWG import."""
        # Check common locations where QGIS DWG import saves
        base = os.path.splitext(os.path.basename(dwg_path))[0]
        candidate_paths = [
            os.path.join(os.path.dirname(dwg_path), f'{base}.gpkg'),
            os.path.join(tempfile.gettempdir(), f'{base}.gpkg'),
        ]

        for gpkg_path in candidate_paths:
            if os.path.exists(gpkg_path):
                self._temp_gpkg = gpkg_path
                self._populate_layer_list_from_gpkg(gpkg_path)
                if self.tree_layers.topLevelItemCount() > 0:
                    return True
        return False

    def _show_dwg_help_dialog(self, input_path, is_dwg):
        """Show help dialog when DWG cannot be read."""
        if is_dwg:
            base = os.path.splitext(os.path.basename(input_path))[0]
            gpkg_suggest = os.path.join(
                os.path.dirname(input_path), f'{base}.gpkg')

            if self.lang == 'vi':
                msg = (
                    '⚠️ Không thể đọc file DWG trực tiếp.\n\n'
                    'File DWG phiên bản mới (AutoCAD 2010+) cần '
                    'import qua QGIS:\n\n'
                    '📋 Các bước:\n'
                    '1. Vào menu: Project → Import/Export →\n'
                    '   Import Layers from DWG/DXF\n'
                    '2. Chọn file DWG của bạn\n'
                    '3. Chọn CRS (VN-2000)\n'
                    f'4. Lưu vào: {gpkg_suggest}\n'
                    '5. Nhấn Import\n\n'
                    '✅ Sau đó mở lại LVT4U DWG Converter,\n'
                    f'chọn file GPKG vừa tạo để xuất SHP.\n\n'
                    '💡 Hoặc dùng AutoCAD/LibreCAD lưu thành\n'
                    'DXF rồi mở lại với LVT4U.'
                )
            else:
                msg = (
                    '⚠️ Cannot read DWG file directly.\n\n'
                    'Modern DWG files (AutoCAD 2010+) require\n'
                    'QGIS built-in import:\n\n'
                    '📋 Steps:\n'
                    '1. Menu: Project → Import/Export →\n'
                    '   Import Layers from DWG/DXF\n'
                    '2. Select your DWG file\n'
                    '3. Choose CRS (VN-2000)\n'
                    f'4. Save to: {gpkg_suggest}\n'
                    '5. Click Import\n\n'
                    '✅ Then reopen LVT4U DWG Converter,\n'
                    f'select the created GPKG file.\n\n'
                    '💡 Or use AutoCAD/LibreCAD to save as DXF.'
                )
        else:
            msg = self._t('No layers found')

        QMessageBox.information(self, 'LVT4U', msg)

        # Offer to open QGIS DWG Import dialog directly
        if is_dwg:
            reply = QMessageBox.question(
                self, 'LVT4U',
                'Mở QGIS Import DWG ngay?\n\n'
                'Open QGIS Import DWG now?'
                if self.lang == 'vi' else
                'Open QGIS Import DWG dialog now?',
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.Yes,
            )
            if reply == QMessageBox.Yes:
                self._open_qgis_dwg_import()

    def _populate_layer_list_from_gpkg(self, gpkg_path):
        """Populate tree widget from GeoPackage sublayers."""
        gpkg_layer = QgsVectorLayer(gpkg_path, 'scan', 'ogr')
        if not gpkg_layer.isValid():
            # Try sublayer listing directly
            pass

        sub_layers = gpkg_layer.dataProvider().subLayers() \
            if gpkg_layer.isValid() else []
        self.tree_layers.clear()

        if not sub_layers:
            # Try listing via OGR metadata
            try:
                from osgeo import ogr
                ds = ogr.Open(gpkg_path)
                if ds:
                    for i in range(ds.GetLayerCount()):
                        lyr = ds.GetLayerByIndex(i)
                        lyr_name = lyr.GetName()
                        feat_count = lyr.GetFeatureCount()
                        geom_name = ogr.GeometryTypeToName(
                            lyr.GetGeomType())
                        display_geom = _GEOM_MAP.get(
                            geom_name, geom_name)

                        item = QTreeWidgetItem()
                        item.setFlags(
                            item.flags() | Qt.ItemIsUserCheckable)
                        item.setCheckState(
                            0,
                            Qt.Checked if feat_count > 0
                            else Qt.Unchecked)
                        item.setText(0, lyr_name)
                        item.setText(1, display_geom)
                        item.setText(2, str(feat_count))
                        item.setData(0, Qt.UserRole, {
                            'name': lyr_name,
                            'geom_type': geom_name,
                            'count': feat_count,
                            'source': 'gpkg',
                        })
                        self.tree_layers.addTopLevelItem(item)
                    ds = None
            except ImportError:
                pass
        else:
            for sub in sub_layers:
                parts = sub.split('!!::!!')
                if len(parts) < 4:
                    continue
                layer_name = parts[1]
                count = parts[2]
                geom_type = parts[3] if len(parts) > 3 else 'Unknown'
                display_geom = _GEOM_MAP.get(geom_type, geom_type)

                item = QTreeWidgetItem()
                item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
                item.setCheckState(
                    0, Qt.Checked if int(count) > 0 else Qt.Unchecked)
                item.setText(0, layer_name)
                item.setText(1, display_geom)
                item.setText(2, str(count))
                item.setData(0, Qt.UserRole, {
                    'name': layer_name,
                    'geom_type': geom_type,
                    'count': int(count),
                    'source': 'gpkg',
                })
                self.tree_layers.addTopLevelItem(item)

        if gpkg_layer.isValid():
            del gpkg_layer

        self._update_layer_summary()
        self.progress.setValue(100)
        QApplication.processEvents()
        self.progress.setVisible(False)
        self.lbl_status.setText("")

        if self.tree_layers.topLevelItemCount() == 0:
            QMessageBox.information(
                self, 'LVT4U', self._t('No layers found'))

    # ------------------------------------------------------------------
    # Convert
    # ------------------------------------------------------------------

    def _convert(self):
        """Convert selected CAD layers to SHP files."""
        input_path = self.txt_input.text().strip()
        output_dir = self.txt_output.text().strip()

        if not input_path:
            QMessageBox.warning(
                self, 'LVT4U', self._t('Select source file first'))
            return
        if not output_dir:
            QMessageBox.warning(
                self, 'LVT4U', self._t('Select output folder'))
            return

        # Get selected layers
        selected = []
        for i in range(self.tree_layers.topLevelItemCount()):
            item = self.tree_layers.topLevelItem(i)
            if item.checkState(0) == Qt.Checked:
                data = item.data(0, Qt.UserRole)
                if data and data.get('count', 0) > 0:
                    selected.append(data)

        if not selected:
            QMessageBox.warning(
                self, 'LVT4U', self._t('No layers selected'))
            return

        os.makedirs(output_dir, exist_ok=True)
        crs = self._get_selected_crs()

        self.progress.setVisible(True)
        self.progress.setValue(0)

        results = []
        total = len(selected)

        try:
            for idx, layer_data in enumerate(selected, 1):
                layer_name = layer_data['name']
                geom_type = layer_data['geom_type']
                source = layer_data.get('source', 'gpkg')

                pct = int((idx - 1) / total * 100)
                self.progress.setValue(pct)
                self.progress.setFormat(
                    f'Layer {idx}/{total}: {layer_name} — %p%')
                self.lbl_status.setText(
                    f"{self._t('Processing...')} {layer_name}")
                QApplication.processEvents()

                # Build layer URI
                if source == 'gpkg' and self._temp_gpkg:
                    uri = f'{self._temp_gpkg}|layername={layer_name}'
                else:
                    uri = (f'{input_path}|layername={layer_name}'
                           f'|geometrytype={geom_type}')

                vlayer = QgsVectorLayer(uri, layer_name, 'ogr')
                if not vlayer.isValid() or vlayer.featureCount() == 0:
                    continue

                # Clean filename
                safe_name = (layer_name
                             .replace(' ', '_')
                             .replace('/', '_')
                             .replace('\\', '_')
                             .replace(':', '_')
                             .replace('*', '_')
                             .replace('?', '_')
                             .replace('"', '_')
                             .replace('<', '_')
                             .replace('>', '_')
                             .replace('|', '_'))
                if self.chk_separate_geom.isChecked():
                    display_geom = _GEOM_MAP.get(geom_type, geom_type)
                    safe_name = f'{safe_name}_{display_geom}'

                shp_path = os.path.join(output_dir, f'{safe_name}.shp')

                # Write to SHP
                options = QgsVectorFileWriter.SaveVectorOptions()
                options.driverName = 'ESRI Shapefile'
                options.fileEncoding = 'UTF-8'
                if crs and crs.isValid() and vlayer.crs().isValid():
                    if vlayer.crs().authid() != crs.authid():
                        options.ct = QgsCoordinateTransform(
                            vlayer.crs(), crs, QgsProject.instance())

                error_code, error_msg, new_file, new_layer = \
                    QgsVectorFileWriter.writeAsVectorFormatV3(
                        vlayer, shp_path,
                        QgsCoordinateTransformContext(), options
                    )

                if error_code == QgsVectorFileWriter.NoError:
                    feat_count = vlayer.featureCount()
                    results.append(
                        (layer_name, geom_type, feat_count, shp_path))

                    # Add to map if requested
                    if self.chk_add_to_map.isChecked():
                        result_layer = QgsVectorLayer(
                            shp_path, safe_name, 'ogr')
                        if result_layer.isValid():
                            QgsProject.instance().addMapLayer(
                                result_layer)

                del vlayer

            self.progress.setValue(100)
            self.progress.setFormat(self._t('Conversion complete'))
            self.lbl_status.setText(self._t('Conversion complete'))
            QApplication.processEvents()

            # Show summary
            if results:
                total_feats = sum(r[2] for r in results)
                msg = (
                    f'{len(results)} {self._t("layers converted")}\n'
                    f'{total_feats} {self._t("features total")}\n\n'
                    f'📂 {output_dir}')
                for name, gtype, count, path in results:
                    display_g = _GEOM_MAP.get(gtype, gtype)
                    msg += f'\n  • {name} ({display_g}) — {count}'
                QMessageBox.information(self, 'LVT4U', msg)
            else:
                QMessageBox.warning(
                    self, 'LVT4U', self._t('No layers found'))

        except Exception as e:
            QMessageBox.critical(
                self, 'LVT4U', f'{self._t("Error")}: {str(e)}')
        finally:
            self.progress.setVisible(False)
            self.lbl_status.setText("")
            # Cleanup temp GPKG
            self._cleanup_temp_gpkg()

    # ------------------------------------------------------------------
    # Cleanup
    # ------------------------------------------------------------------

    def _cleanup_temp_gpkg(self):
        """Remove temporary GeoPackage files."""
        if self._temp_gpkg:
            try:
                for ext in ['', '-wal', '-shm']:
                    p = self._temp_gpkg + ext
                    if os.path.exists(p):
                        os.remove(p)
            except OSError:
                pass
            self._temp_gpkg = None

    # ------------------------------------------------------------------
    # Help
    # ------------------------------------------------------------------

    def _show_help(self):
        """Show the help dialog for this module."""
        dlg = HelpDialog('dwg_converter', self)
        dlg.exec_()

    # ------------------------------------------------------------------
    # Close event
    # ------------------------------------------------------------------

    def closeEvent(self, event):
        """Clean up temp files on close."""
        self._cleanup_temp_gpkg()
        super().closeEvent(event)
