#!/usr/bin/env python3
"""
Database Manager Widget - vzug-e-hinge
=======================================
Comprehensive database management interface for inspecting and managing sessions.

Features:
- Session browser with sort/filter/search
- Session details with editable notes
- Rename, delete, and export sessions
- Delete individual runs
- Session statistics

Author: Kynsight
Version: 1.0.0
"""

from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QSplitter,
    QTableWidget, QTableWidgetItem, QHeaderView,
    QLabel, QPushButton, QLineEdit, QTextEdit,
    QGroupBox, QMessageBox, QInputDialog, QFileDialog,
    QCheckBox, QListWidget, QListWidgetItem
)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont
from datetime import datetime
import sqlite3


class DatabaseManagerWidget(QWidget):
    """
    Database Manager Widget.

    Provides comprehensive session management:
    - Browse all sessions
    - View session details and runs
    - Rename, delete, export sessions
    - Add notes to sessions
    - Delete individual runs
    """

    def __init__(self, db_manager):
        """
        Initialize Database Manager Widget.

        Args:
            db_manager: DatabaseManager instance
        """
        super().__init__()
        self.db_manager = db_manager
        self.selected_session_id = None
        self._ensure_notes_column()
        self._init_ui()
        self._load_sessions()

    def _ensure_notes_column(self):
        """Ensure notes column exists in sessions table (migration)."""
        try:
            conn = self.db_manager.get_connection()

            # Check if notes column exists
            cursor = conn.execute("PRAGMA table_info(sessions)")
            columns = [row[1] for row in cursor.fetchall()]

            if 'notes' not in columns:
                # Add notes column
                conn.execute("ALTER TABLE sessions ADD COLUMN notes TEXT DEFAULT ''")
                conn.commit()
                print("[INFO] Added 'notes' column to sessions table")
        except Exception as e:
            print(f"[WARN] Failed to add notes column: {e}")

    def _init_ui(self):
        """Initialize user interface."""
        layout = QVBoxLayout()
        self.setLayout(layout)

        # Main splitter (left: session browser, right: details)
        splitter = QSplitter(Qt.Orientation.Horizontal)
        layout.addWidget(splitter)

        # Left panel: Session Browser
        left_panel = self._create_session_browser()
        splitter.addWidget(left_panel)

        # Right panel: Session Details
        right_panel = self._create_session_details()
        splitter.addWidget(right_panel)

        # Set splitter ratio (40% left, 60% right)
        splitter.setStretchFactor(0, 4)
        splitter.setStretchFactor(1, 6)

    def _create_session_browser(self):
        """Create session browser panel (left side)."""
        panel = QWidget()
        layout = QVBoxLayout()
        panel.setLayout(layout)

        # Search and filter
        search_layout = QHBoxLayout()
        search_layout.addWidget(QLabel("Search:"))
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("Filter sessions by name...")
        self.search_input.textChanged.connect(self._filter_sessions)
        search_layout.addWidget(self.search_input)
        layout.addLayout(search_layout)

        # Session table
        self.session_table = QTableWidget()
        self.session_table.setColumnCount(5)
        self.session_table.setHorizontalHeaderLabels([
            "Select", "Session Name", "Date", "Runs", "Size (MB)"
        ])

        # Configure table
        header = self.session_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(4, QHeaderView.ResizeMode.ResizeToContents)

        self.session_table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
        self.session_table.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
        self.session_table.itemSelectionChanged.connect(self._on_session_selected)
        self.session_table.setSortingEnabled(True)

        layout.addWidget(self.session_table)

        # Batch action buttons
        btn_layout = QHBoxLayout()

        self.btn_select_all = QPushButton("Select All")
        self.btn_select_all.clicked.connect(self._select_all_sessions)
        btn_layout.addWidget(self.btn_select_all)

        self.btn_clear_selection = QPushButton("Clear")
        self.btn_clear_selection.clicked.connect(self._clear_selection)
        btn_layout.addWidget(self.btn_clear_selection)

        btn_layout.addStretch()

        self.btn_delete_selected = QPushButton("Delete Selected")
        self.btn_delete_selected.clicked.connect(self._delete_selected_sessions)
        btn_layout.addWidget(self.btn_delete_selected)

        self.btn_refresh = QPushButton("Refresh")
        self.btn_refresh.clicked.connect(self._load_sessions)
        btn_layout.addWidget(self.btn_refresh)

        layout.addLayout(btn_layout)

        return panel

    def _create_session_details(self):
        """Create session details panel (right side)."""
        panel = QWidget()
        layout = QVBoxLayout()
        panel.setLayout(layout)

        # Session info group
        info_group = QGroupBox("Session Information")
        info_layout = QVBoxLayout()
        info_group.setLayout(info_layout)

        # Session ID
        self.label_session_id = QLabel("No session selected")
        self.label_session_id.setFont(QFont("Arial", 10, QFont.Weight.Bold))
        info_layout.addWidget(self.label_session_id)

        # Created date
        self.label_created = QLabel("")
        info_layout.addWidget(self.label_created)

        # Notes
        notes_layout = QHBoxLayout()
        notes_layout.addWidget(QLabel("Notes:"))
        self.text_notes = QTextEdit()
        self.text_notes.setMaximumHeight(80)
        self.text_notes.setPlaceholderText("Add notes or description for this session...")
        self.text_notes.textChanged.connect(self._on_notes_changed)
        notes_layout.addWidget(self.text_notes)
        info_layout.addLayout(notes_layout)

        # Session actions
        action_layout = QHBoxLayout()

        self.btn_rename = QPushButton("Rename Session")
        self.btn_rename.clicked.connect(self._rename_session)
        action_layout.addWidget(self.btn_rename)

        self.btn_delete = QPushButton("Delete Session")
        self.btn_delete.clicked.connect(self._delete_session)
        action_layout.addWidget(self.btn_delete)

        self.btn_export = QPushButton("Export CSV")
        self.btn_export.clicked.connect(self._export_session)
        action_layout.addWidget(self.btn_export)

        action_layout.addStretch()
        info_layout.addLayout(action_layout)

        layout.addWidget(info_group)

        # Runs group
        runs_group = QGroupBox("Runs in Session")
        runs_layout = QVBoxLayout()
        runs_group.setLayout(runs_layout)

        self.runs_list = QListWidget()
        runs_layout.addWidget(self.runs_list)

        # Run actions
        run_action_layout = QHBoxLayout()

        self.btn_delete_runs = QPushButton("Delete Selected Runs")
        self.btn_delete_runs.clicked.connect(self._delete_selected_runs)
        run_action_layout.addWidget(self.btn_delete_runs)

        run_action_layout.addStretch()
        runs_layout.addLayout(run_action_layout)

        layout.addWidget(runs_group)

        # Statistics group
        stats_group = QGroupBox("Session Statistics")
        stats_layout = QVBoxLayout()
        stats_group.setLayout(stats_layout)

        self.label_stats = QLabel("Select a session to view statistics")
        self.label_stats.setFont(QFont("Monospace", 9))
        stats_layout.addWidget(self.label_stats)

        layout.addWidget(stats_group)

        # Disable all controls initially
        self._set_details_enabled(False)

        return panel

    def _set_details_enabled(self, enabled: bool):
        """Enable or disable detail panel controls."""
        self.text_notes.setEnabled(enabled)
        self.btn_rename.setEnabled(enabled)
        self.btn_delete.setEnabled(enabled)
        self.btn_export.setEnabled(enabled)
        self.btn_delete_runs.setEnabled(enabled)

    def _load_sessions(self):
        """Load all sessions from database."""
        try:
            conn = self.db_manager.get_connection()

            cursor = conn.execute("""
                SELECT
                    session_id,
                    session_name,
                    created_at,
                    total_runs,
                    notes,
                    (SELECT COUNT(*) FROM telemetry_decoded WHERE telemetry_decoded.session_id = sessions.session_id) as data_points
                FROM sessions
                ORDER BY created_at DESC
            """)

            sessions = cursor.fetchall()

            # Store all sessions for filtering
            self.all_sessions = sessions

            # Display sessions
            self._display_sessions(sessions)

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to load sessions:\n{str(e)}")

    def _display_sessions(self, sessions):
        """Display sessions in table."""
        self.session_table.setSortingEnabled(False)
        self.session_table.setRowCount(0)

        for row_idx, session in enumerate(sessions):
            session_id, session_name, created_at, total_runs, notes, data_points = session

            self.session_table.insertRow(row_idx)

            # Checkbox
            checkbox = QCheckBox()
            checkbox_widget = QWidget()
            checkbox_layout = QHBoxLayout(checkbox_widget)
            checkbox_layout.addWidget(checkbox)
            checkbox_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
            checkbox_layout.setContentsMargins(0, 0, 0, 0)
            self.session_table.setCellWidget(row_idx, 0, checkbox_widget)

            # Session name
            name_item = QTableWidgetItem(session_name)
            name_item.setData(Qt.ItemDataRole.UserRole, session_id)
            self.session_table.setItem(row_idx, 1, name_item)

            # Date
            date_item = QTableWidgetItem(created_at)
            self.session_table.setItem(row_idx, 2, date_item)

            # Runs
            runs_item = QTableWidgetItem(str(total_runs))
            runs_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
            self.session_table.setItem(row_idx, 3, runs_item)

            # Size (estimate: ~100 bytes per data point)
            size_mb = (data_points * 100) / (1024 * 1024)
            size_item = QTableWidgetItem(f"{size_mb:.2f}")
            size_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
            self.session_table.setItem(row_idx, 4, size_item)

        self.session_table.setSortingEnabled(True)

    def _filter_sessions(self):
        """Filter sessions based on search text."""
        search_text = self.search_input.text().lower()

        if not search_text:
            # Show all sessions
            self._display_sessions(self.all_sessions)
        else:
            # Filter sessions
            filtered = [s for s in self.all_sessions if search_text in s[1].lower()]
            self._display_sessions(filtered)

    def _on_session_selected(self):
        """Handle session selection."""
        selected_items = self.session_table.selectedItems()

        if not selected_items:
            self._set_details_enabled(False)
            self.selected_session_id = None
            self.label_session_id.setText("No session selected")
            self.label_created.setText("")
            self.text_notes.clear()
            self.runs_list.clear()
            self.label_stats.setText("Select a session to view statistics")
            return

        # Get session ID from selected row
        row = selected_items[0].row()
        session_id = self.session_table.item(row, 1).data(Qt.ItemDataRole.UserRole)

        self.selected_session_id = session_id
        self._load_session_details(session_id)
        self._set_details_enabled(True)

    def _load_session_details(self, session_id: str):
        """Load details for selected session."""
        try:
            conn = self.db_manager.get_connection()

            # Get session info
            cursor = conn.execute("""
                SELECT session_id, session_name, created_at, total_runs, notes
                FROM sessions
                WHERE session_id = ?
            """, (session_id,))

            session = cursor.fetchone()

            if not session:
                return

            session_id, session_name, created_at, total_runs, notes = session

            # Update UI
            self.label_session_id.setText(f"Session: {session_name}")
            self.label_created.setText(f"Created: {created_at}")

            # Block signals while setting notes to avoid triggering save
            self.text_notes.blockSignals(True)
            self.text_notes.setPlainText(notes or "")
            self.text_notes.blockSignals(False)

            # Load runs
            self._load_runs(session_id)

            # Load statistics
            self._load_statistics(session_id)

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to load session details:\n{str(e)}")

    def _load_runs(self, session_id: str):
        """Load runs for selected session."""
        try:
            conn = self.db_manager.get_connection()

            cursor = conn.execute("""
                SELECT DISTINCT
                    t.run_no,
                    COALESCE(u.command_name, i.command_name, 'Unknown') as command_name,
                    COUNT(*) as sample_count
                FROM telemetry_decoded t
                LEFT JOIN uart_commands u ON t.run_command_id = u.command_id
                LEFT JOIN i2c_commands i ON t.run_command_id = i.command_id
                WHERE t.session_id = ?
                GROUP BY t.run_no
                ORDER BY t.run_no
            """, (session_id,))

            runs = cursor.fetchall()

            self.runs_list.clear()

            for run_no, command_name, sample_count in runs:
                item = QListWidgetItem(f"Run {run_no}: {command_name} ({sample_count:,} samples)")
                item.setData(Qt.ItemDataRole.UserRole, run_no)
                item.setCheckState(Qt.CheckState.Unchecked)
                self.runs_list.addItem(item)

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to load runs:\n{str(e)}")

    def _load_statistics(self, session_id: str):
        """Load statistics for selected session."""
        try:
            conn = self.db_manager.get_connection()

            # Get statistics
            cursor = conn.execute("""
                SELECT
                    COUNT(DISTINCT run_no) as total_runs,
                    COUNT(*) as total_samples,
                    MIN(t_ns) as start_time,
                    MAX(t_ns) as end_time
                FROM telemetry_decoded
                WHERE session_id = ?
            """, (session_id,))

            stats = cursor.fetchone()

            if stats and stats[0] > 0:
                total_runs, total_samples, start_time, end_time = stats

                # Calculate duration
                duration_ns = end_time - start_time
                duration_s = duration_ns / 1_000_000_000

                # Format statistics
                stats_text = f"• Total Runs: {total_runs}\n"
                stats_text += f"• Total Samples: {total_samples:,}\n"
                stats_text += f"• Avg Samples/Run: {total_samples // total_runs:,}\n"
                stats_text += f"• Duration: {duration_s:.1f} seconds\n"

                self.label_stats.setText(stats_text)
            else:
                self.label_stats.setText("No data available for this session")

        except Exception as e:
            self.label_stats.setText(f"Error loading statistics: {str(e)}")

    def _on_notes_changed(self):
        """Handle notes text change - auto-save."""
        if not self.selected_session_id:
            return

        notes = self.text_notes.toPlainText()

        try:
            conn = self.db_manager.get_connection()
            conn.execute("""
                UPDATE sessions
                SET notes = ?
                WHERE session_id = ?
            """, (notes, self.selected_session_id))
            conn.commit()
        except Exception as e:
            print(f"Failed to save notes: {e}")

    def _rename_session(self):
        """Rename selected session."""
        if not self.selected_session_id:
            return

        # Get current name
        current_name = self.label_session_id.text().replace("Session: ", "")

        # Ask for new name
        new_name, ok = QInputDialog.getText(
            self,
            "Rename Session",
            "Enter new session name:",
            text=current_name
        )

        if not ok or not new_name.strip():
            return

        try:
            conn = self.db_manager.get_connection()

            # Update session name in sessions table
            conn.execute("""
                UPDATE sessions
                SET session_name = ?
                WHERE session_id = ?
            """, (new_name.strip(), self.selected_session_id))

            # Update session name in telemetry_raw table
            conn.execute("""
                UPDATE telemetry_raw
                SET session_name = ?
                WHERE session_id = ?
            """, (new_name.strip(), self.selected_session_id))

            # Update session name in telemetry_decoded table
            conn.execute("""
                UPDATE telemetry_decoded
                SET session_name = ?
                WHERE session_id = ?
            """, (new_name.strip(), self.selected_session_id))

            conn.commit()

            # Reload sessions
            self._load_sessions()

            # Reload details
            self._load_session_details(self.selected_session_id)

            QMessageBox.information(self, "Success", "Session renamed successfully!")

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to rename session:\n{str(e)}")

    def _delete_session(self):
        """Delete selected session."""
        if not self.selected_session_id:
            return

        # Confirm
        reply = QMessageBox.question(
            self,
            "Confirm Delete",
            f"Delete session '{self.selected_session_id}'?\n\n"
            "This will delete all runs and data for this session.\n"
            "This action cannot be undone!",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
        )

        if reply != QMessageBox.StandardButton.Yes:
            return

        try:
            conn = self.db_manager.get_connection()
            conn.execute("DELETE FROM telemetry_decoded WHERE session_id = ?", (self.selected_session_id,))
            conn.execute("DELETE FROM telemetry_raw WHERE session_id = ?", (self.selected_session_id,))
            conn.execute("DELETE FROM sessions WHERE session_id = ?", (self.selected_session_id,))
            conn.commit()

            self.selected_session_id = None
            self._load_sessions()
            self._set_details_enabled(False)

            QMessageBox.information(self, "Success", "Session deleted successfully!")

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to delete session:\n{str(e)}")

    def _export_session(self):
        """Export selected session to CSV."""
        if not self.selected_session_id:
            return

        # Get save location
        filename = f"{self.selected_session_id}_export.csv"
        filepath, _ = QFileDialog.getSaveFileName(
            self,
            "Export Session",
            filename,
            "CSV Files (*.csv);;All Files (*)"
        )

        if not filepath:
            return

        try:
            import csv

            conn = self.db_manager.get_connection()

            # Get all data for session
            cursor = conn.execute("""
                SELECT
                    session_id, session_name, run_no, t_ns, time_ms,
                    motor_current, encoder_value, relative_encoder_value,
                    v24_pec_diff, pwm,
                    i2c_raw14, i2c_zero_raw14, i2c_delta_raw14,
                    i2c_angle_deg, i2c_zero_angle_deg,
                    angular_velocity, angular_acceleration
                FROM telemetry_decoded
                WHERE session_id = ?
                ORDER BY run_no, t_ns
            """, (self.selected_session_id,))

            rows = cursor.fetchall()

            # Write to CSV
            with open(filepath, 'w', newline='') as f:
                writer = csv.writer(f)

                # Header
                writer.writerow([
                    'session_id', 'session_name', 'run_no', 't_ns', 'time_ms',
                    'motor_current', 'encoder_value', 'relative_encoder_value',
                    'v24_pec_diff', 'pwm',
                    'i2c_raw14', 'i2c_zero_raw14', 'i2c_delta_raw14',
                    'i2c_angle_deg', 'i2c_zero_angle_deg',
                    'angular_velocity', 'angular_acceleration'
                ])

                # Data
                writer.writerows(rows)

            QMessageBox.information(
                self,
                "Export Complete",
                f"Session exported successfully!\n\n"
                f"File: {filepath}\n"
                f"Rows: {len(rows):,}"
            )

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to export session:\n{str(e)}")

    def _delete_selected_runs(self):
        """Delete selected runs from session."""
        if not self.selected_session_id:
            return

        # Get checked runs
        selected_runs = []
        for i in range(self.runs_list.count()):
            item = self.runs_list.item(i)
            if item.checkState() == Qt.CheckState.Checked:
                run_no = item.data(Qt.ItemDataRole.UserRole)
                selected_runs.append(run_no)

        if not selected_runs:
            QMessageBox.warning(self, "No Selection", "Please select runs to delete.")
            return

        # Confirm
        reply = QMessageBox.question(
            self,
            "Confirm Delete",
            f"Delete {len(selected_runs)} run(s) from session?\n\n"
            "This will permanently delete all data for these runs.\n"
            "This action cannot be undone!",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
        )

        if reply != QMessageBox.StandardButton.Yes:
            return

        try:
            conn = self.db_manager.get_connection()

            for run_no in selected_runs:
                conn.execute("""
                    DELETE FROM telemetry_decoded
                    WHERE session_id = ? AND run_no = ?
                """, (self.selected_session_id, run_no))

                conn.execute("""
                    DELETE FROM telemetry_raw
                    WHERE session_id = ? AND run_no = ?
                """, (self.selected_session_id, run_no))

            # Update total_runs in sessions table
            cursor = conn.execute("""
                SELECT COUNT(DISTINCT run_no)
                FROM telemetry_decoded
                WHERE session_id = ?
            """, (self.selected_session_id,))

            new_run_count = cursor.fetchone()[0]

            conn.execute("""
                UPDATE sessions
                SET total_runs = ?
                WHERE session_id = ?
            """, (new_run_count, self.selected_session_id))

            conn.commit()

            # Reload details
            self._load_session_details(self.selected_session_id)
            self._load_sessions()

            QMessageBox.information(self, "Success", f"Deleted {len(selected_runs)} run(s) successfully!")

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to delete runs:\n{str(e)}")

    def _select_all_sessions(self):
        """Select all checkboxes in session table."""
        for row in range(self.session_table.rowCount()):
            checkbox_widget = self.session_table.cellWidget(row, 0)
            checkbox = checkbox_widget.findChild(QCheckBox)
            if checkbox:
                checkbox.setChecked(True)

    def _clear_selection(self):
        """Clear all checkboxes in session table."""
        for row in range(self.session_table.rowCount()):
            checkbox_widget = self.session_table.cellWidget(row, 0)
            checkbox = checkbox_widget.findChild(QCheckBox)
            if checkbox:
                checkbox.setChecked(False)

    def _delete_selected_sessions(self):
        """Delete all checked sessions."""
        # Get checked sessions
        selected_sessions = []
        for row in range(self.session_table.rowCount()):
            checkbox_widget = self.session_table.cellWidget(row, 0)
            checkbox = checkbox_widget.findChild(QCheckBox)
            if checkbox and checkbox.isChecked():
                session_id = self.session_table.item(row, 1).data(Qt.ItemDataRole.UserRole)
                session_name = self.session_table.item(row, 1).text()
                selected_sessions.append((session_id, session_name))

        if not selected_sessions:
            QMessageBox.warning(self, "No Selection", "Please select sessions to delete.")
            return

        # Confirm
        session_list = "\n".join([f"• {name}" for _, name in selected_sessions])
        reply = QMessageBox.question(
            self,
            "Confirm Delete",
            f"Delete {len(selected_sessions)} session(s)?\n\n{session_list}\n\n"
            "This will permanently delete all data for these sessions.\n"
            "This action cannot be undone!",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
        )

        if reply != QMessageBox.StandardButton.Yes:
            return

        try:
            conn = self.db_manager.get_connection()

            for session_id, _ in selected_sessions:
                conn.execute("DELETE FROM telemetry_decoded WHERE session_id = ?", (session_id,))
                conn.execute("DELETE FROM telemetry_raw WHERE session_id = ?", (session_id,))
                conn.execute("DELETE FROM sessions WHERE session_id = ?", (session_id,))

            conn.commit()

            # Vacuum database
            self.db_manager.vacuum()

            self.selected_session_id = None
            self._load_sessions()
            self._set_details_enabled(False)

            QMessageBox.information(
                self,
                "Success",
                f"Deleted {len(selected_sessions)} session(s) successfully!\n\n"
                "Database vacuumed."
            )

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to delete sessions:\n{str(e)}")
