"""
Log Viewer - Real-time log display and monitoring
"""

from PyQt5.QtWidgets import (
    QWidget, QVBoxLayout, QTextEdit, QLabel, QSizePolicy, QTabWidget
)
from PyQt5.QtCore import Qt, pyqtSignal, QTimer
from PyQt5.QtGui import QTextCursor, QFont, QTextCharFormat
from pathlib import Path
import datetime
import html
import os


class LogViewer(QWidget):
    """Widget for viewing translation logs and monitoring progress."""
    
    log_updated = pyqtSignal(str)
    mismatch_detected = pyqtSignal()  # Emitted each time a [MISMATCH] header line is seen
    
    def __init__(self):
        super().__init__()
        # Lightweight append-only log viewer. Logs are provided by the
        # translation worker via signals so we don't poll files or provide
        # controls here. This keeps the UI responsive.
        self._error_count = 0
        self.init_ui()
        
    def init_ui(self):
        """Initialize the user interface."""
        layout = QVBoxLayout()
        # Remove internal margins so header aligns with the left column's header
        layout.setContentsMargins(0, 0, 0, 0)

        # Simple header to match left-side styling (use same look as create_section_header)
        header = QLabel("📝 Translation Log")
        header.setStyleSheet("""
            QLabel {
                font-size: 13px;
                font-weight: bold;
                color: #007acc;
                padding: 8px 0px 5px 0px;
                background-color: transparent;
            }
        """)
        layout.addWidget(header, 0)

        # Match spacing used in left column so the gap between header and
        # content lines up visually.
        layout.setSpacing(8)

        # Tab widget for All / Errors views
        self._tab_widget = QTabWidget()
        self._tab_widget.setStyleSheet("""
            QTabWidget::pane {
                border: 1px solid #555555;
                background-color: #1e1e1e;
            }
            QTabBar::tab {
                background-color: #2d2d2d;
                color: #cccccc;
                padding: 5px 12px;
                border: 1px solid #555555;
                border-bottom: none;
            }
            QTabBar::tab:selected {
                background-color: #1e1e1e;
                color: #ffffff;
            }
            QTabBar::tab:hover {
                background-color: #3a3a3a;
            }
        """)
        self._tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        _log_font = QFont("Consolas", 10)
        _text_edit_style = """
            QTextEdit {
                background-color: #1e1e1e;
                color: #ffffff;
                border: none;
            }
        """

        # --- All tab ---
        self.log_display = QTextEdit()
        self.log_display.setReadOnly(True)
        self.log_display.setFont(_log_font)
        self.log_display.setStyleSheet(_text_edit_style)
        self.log_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self._tab_widget.addTab(self.log_display, "All")

        # --- Errors tab (MISMATCH + API_ERROR only) ---
        self.error_display = QTextEdit()
        self.error_display.setReadOnly(True)
        self.error_display.setFont(_log_font)
        self.error_display.setStyleSheet(_text_edit_style)
        self.error_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self._tab_widget.addTab(self.error_display, "Errors")

        layout.addWidget(self._tab_widget, 1)

        # Lightweight status label
        self.status_label = QLabel("Ready")
        self.status_label.setStyleSheet("color: #cccccc; padding: 5px;")
        layout.addWidget(self.status_label, 0)

        self.setLayout(layout)
        # Tail-related fields
        self._tail_timer = QTimer(self)
        self._tail_timer.timeout.connect(self._poll_tail)
        self._tail_interval = 300  # ms
        self._tail_f = None
        self._tail_buffer = ""  # Buffer for incomplete lines
        # Default: prefer the most recent file in log/history, else legacy path
        try:
            latest = self._latest_history_file()
        except Exception:
            latest = None
        self._tail_path = latest or Path("log/translationHistory.txt")
        
    def toggle_auto_refresh(self, enabled):
        """Toggle auto-refresh functionality."""
        # No-op: controls removed in simplified viewer
        pass
            
    def adjust_refresh_interval(self):
        """Adjust refresh interval based on current log file size."""
        # No-op in simplified viewer
        pass

    def update_max_lines(self, value):
        """No-op in simplified viewer (controls removed)."""
        pass
            
    def load_selected_log(self):
        """Load the currently selected log file."""
        # No-op in simplified viewer
        pass
            
    def load_log_file(self, file_path):
        """Load content from a specific log file with optimization."""
        # File-based loading removed in simplified viewer
        pass
            
    def read_file_tail(self, file_path, max_lines):
        """Efficiently read the last N lines of a large file."""
        # No-op in simplified viewer
        return ""
            
    def refresh_logs(self):
        """Refresh the current log display - only if tab is visible and file changed."""
        # No-op for simplified viewer
        pass
        
    def clear_log(self):
        """Clear the log display and reset tracking."""
        self.log_display.clear()
        # Reset the char format so the red color from previous [MISMATCH]
        # HTML spans doesn't bleed into new plain-text appends.
        self.log_display.setCurrentCharFormat(QTextCharFormat())
        self.error_display.clear()
        self.error_display.setCurrentCharFormat(QTextCharFormat())
        self._error_count = 0
        self._tab_widget.setTabText(1, "Errors")
        self.status_label.setText("Log cleared")
        # Reset tail file pointer and buffer if active
        self._tail_buffer = ""
        if self._tail_f:
            try:
                # Move pointer to end so we continue only with new lines
                self._tail_f.seek(0, os.SEEK_END)
            except Exception:
                pass

    def start_tail(self, file_path: str | Path = None, interval_ms: int = None):
        """Start tailing the given log file and append new lines as they arrive.

        By default tails the most recent file in `log/history/` (if present),
        otherwise falls back to `log/translationHistory.txt`.
        Seeks to the end so only new lines after this call are shown.
        """
        if file_path:
            self._tail_path = Path(file_path)
        if interval_ms:
            self._tail_interval = interval_ms

        # Ensure directory exists but don't create the file
        try:
            self._tail_path.parent.mkdir(parents=True, exist_ok=True)
        except Exception:
            pass

        # Close previous handle
        if self._tail_f:
            try:
                self._tail_f.close()
            except Exception:
                pass
            self._tail_f = None

        # Only open the file if it exists
        # If it doesn't exist yet, the timer will keep checking
        if self._tail_path.exists():
            try:
                self._tail_f = open(self._tail_path, 'r', encoding='utf-8', errors='ignore')
                # Seek to end so we only read new lines
                self._tail_f.seek(0, os.SEEK_END)
            except Exception as e:
                self.status_label.setText(f"Log error: {str(e)}")
                self._tail_f = None
                return

        self._tail_timer.start(self._tail_interval)

    def stop_tail(self):
        """Stop tailing the log file and close internal file handle.

        Performs a final drain so no data written before this call is lost.
        """
        try:
            self._tail_timer.stop()
        except Exception:
            pass
        # Final drain: read any remaining data before closing the handle
        try:
            self._poll_tail()
        except Exception:
            pass
        # Flush any leftover partial line sitting in the buffer
        if self._tail_buffer and self._tail_buffer.strip():
            try:
                self.append_log_message(self._tail_buffer)
            except Exception:
                pass
            self._tail_buffer = ""
        if self._tail_f:
            try:
                self._tail_f.close()
            except Exception:
                pass
            self._tail_f = None

    def _poll_tail(self):
        """Timer callback: read any new data from the tailed file and append it."""
        # If file handle doesn't exist yet, try to open it if the file was created
        if not self._tail_f and self._tail_path.exists():
            try:
                self._tail_f = open(self._tail_path, 'r', encoding='utf-8', errors='ignore')
                # Read from the beginning since this is a per-run file that was
                # created after we started tailing — we want all its content.
            except Exception:
                pass  # Will try again next poll
        
        if not self._tail_f:
            return
        try:
            new_data = self._tail_f.read()
            if not new_data:
                return
            
            # Combine buffer with new data
            combined = self._tail_buffer + new_data
            
            # Split by newlines, keeping the separator info
            lines = combined.split('\n')
            
            # If the data ends with a newline, the last element will be empty
            # Otherwise, it's an incomplete line that should be buffered
            if combined.endswith('\n'):
                # All lines are complete
                self._tail_buffer = ""
                complete_lines = lines[:-1]  # Exclude the empty last element
            else:
                # Last line is incomplete, save it for next time
                self._tail_buffer = lines[-1]
                complete_lines = lines[:-1]
            
            # Append complete lines to the display
            for line in complete_lines:
                if line.strip():
                    self.append_log_message(line)
        except Exception:
            # If anything goes wrong, stop the tail to avoid repeated errors
            self.stop_tail()
        
    def append_log_message(self, message):
        """Append a message to the log display.
        
        Lines containing '[MISMATCH]' or '[API_ERROR]' are also routed to the
        Errors filter tab so they are never lost in a fast-scrolling log.
        """
        escaped = html.escape(message)
        if "[MISMATCH]" in message:
            html_msg = f'<span style="color: #ff4444;">{escaped}</span>'
            self.log_display.append(html_msg)
            self.error_display.append(html_msg)
            self._error_count += 1
            self._tab_widget.setTabText(1, f"Errors ({self._error_count})")
            # Counting is handled via stdout MISMATCH_EVENT markers in
            # TranslationTab.append_log — the log viewer only handles display.
        elif "[API_ERROR]" in message:
            html_msg = f'<span style="color: #ffaa00;">{escaped}</span>'
            self.log_display.append(html_msg)
            self.error_display.append(html_msg)
            self._error_count += 1
            self._tab_widget.setTabText(1, f"Errors ({self._error_count})")
        elif '\u274c' in message:
            # Worker-level error (❌ prefix) — show in both All and Errors tabs.
            html_msg = f'<span style="color: #ff6666;">{escaped}</span>'
            self.log_display.append(html_msg)
            self.error_display.append(html_msg)
            self._error_count += 1
            self._tab_widget.setTabText(1, f"Errors ({self._error_count})")
        else:
            # Explicitly wrap in a white span so Qt doesn't inherit red from
            # a preceding [MISMATCH] HTML append.
            self.log_display.append(f'<span style="color: #ffffff;">{escaped}</span>')

        # Scroll to bottom of whichever tab is active; always scroll the
        # background error display too so it stays at the latest entry.
        for display in (self.log_display, self.error_display):
            cursor = display.textCursor()
            cursor.movePosition(QTextCursor.End)
            display.setTextCursor(cursor)
        
    def get_current_log_content(self):
        """Get the current log content."""
        return self.log_display.toPlainText()
        
    def showEvent(self, event):
        """Handle widget show event - resume refresh if auto-refresh is enabled."""
        super().showEvent(event)
        # No-op for simplified viewer
            
    def hideEvent(self, event):
        """Handle widget hide event - pause refresh to save resources."""
        super().hideEvent(event)
        # No-op for simplified viewer

    def _latest_history_file(self):
        """Return the most recent file in log/history or None if not found."""
        try:
            hist_dir = Path("log") / "history"
            # Ensure history directory exists so callers can rely on it
            hist_dir.mkdir(parents=True, exist_ok=True)
            files = [p for p in hist_dir.iterdir() if p.is_file()]
            if not files:
                return None
            return max(files, key=lambda p: p.stat().st_mtime)
        except Exception:
            return None
