#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
qbrowser.py - A Qt6-based browser that wraps and extends the Playwright browser class

This module provides a Qt6-based browser interface that wraps the Playwright browser
API and launches Chromium browser instances without decorations using the --app flag.
Each new page is opened as a new tab in the Qt6 window, and the interface includes
URL input, home button, and browser extension management.
"""

import os
import sys
import subprocess
import tempfile
import json
import shutil
import asyncio
import platform
import signal
from pathlib import Path
from typing import Dict, List, Optional, Union, Callable, Any
import configparser
from functools import partial

import re

from PyQt6.QtCore import Qt, QUrl, QProcess, pyqtSignal, QSize, QRect, QTimer, QMimeData, QPoint, QByteArray, QBuffer
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtGui import QIcon, QAction, QKeySequence, QFont, QColor, QPainter, QDrag, QPixmap
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QTabWidget, QToolBar, QLineEdit,
    QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QDialog,
    QLabel, QListWidget, QListWidgetItem, QCheckBox, QMenu,
    QSizePolicy, QStyle, QFrame, QSplitter, QMessageBox, QStatusBar, QTabBar, QStyleOptionTab, QFileDialog,
    QInputDialog, QTextEdit
)
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import (
    QWebEngineProfile, QWebEngineSettings, QWebEnginePage, QWebEngineUrlSchemeHandler,
    QWebEngineUrlRequestJob, QWebEngineUrlRequestInterceptor, QWebEngineUrlScheme,
    QWebEngineScript
)
import mimetypes

# Import Playwright for API compatibility
from playwright.async_api import async_playwright, Browser as PlaywrightBrowser
from playwright.async_api import Page as PlaywrightPage
from playwright.async_api import BrowserContext as PlaywrightBrowserContext



class QPlaywrightBrowser:
    """
    A class that wraps the Playwright browser API and uses Qt6Browser internally.
    This class exposes the Playwright API while using the Qt6 browser for display.
    """
    def __init__(self):
        self.app = QApplication.instance() or QApplication(sys.argv)
        self.browser_ui = None
        self.playwright = None
        self.browser = None
        self.contexts = {}
        self.event_loop = None
    
    async def launch(self, **kwargs):
        """Launch a new browser instance."""
        # Initialize Playwright if not already done
        if not self.playwright:
            self.playwright = await async_playwright().start()
        
        # Create the Qt browser UI
        if not self.browser_ui:
            self.browser_ui = Browser()
            self.browser_ui.show()
        
        # Launch the actual Playwright browser (hidden)
        # We'll use this for API compatibility but display in Qt
        self.browser = await self.playwright.chromium.launch(
            headless=True,  # Run headless since we're displaying in Qt
            args = [
               '--headless=new',
               '--enable-features=Autofill',
               ]+os.environ.get('QTWEBENGINE_CHROMIUM_FLAGS').split(),
            **kwargs
        )
        #
        
        # Create a default context
        default_context = await self.browser.new_context()
        self.contexts["default"] = default_context
        
        return self
    
    async def new_context(self, **kwargs):
        """Create a new browser context."""
        if not self.browser:
            await self.launch()
        
        # Create a new context in the Playwright browser
        context_id = f"context_{len(self.contexts)}"
        context = await self.browser.new_context(**kwargs)
        self.contexts[context_id] = context
        
        # Return a wrapper that provides both Playwright API and Qt UI
        return QPlaywrightBrowserContext(self, context, context_id)
    
    async def new_page(self, url="about:blank"):
        """Create a new page in the default context."""
        if "default" not in self.contexts:
            await self.launch()
        
        # Create a new page in the Playwright browser
        pw_page = await self.contexts["default"].new_page()
        await pw_page.goto(url)
        
        # Create a new tab in the Qt browser
        qt_tab = self.browser_ui.new_page(url)
        qt_tab.page = pw_page
        
        # Return a wrapper that provides both Playwright API and Qt UI
        return QPlaywrightPage(pw_page, qt_tab)
    
    async def close(self):
        """Close the browser."""
        # Close all Playwright contexts and browser
        if self.browser:
            for context_id, context in self.contexts.items():
                await context.close()
            await self.browser.close()
            self.contexts = {}
            self.browser = None
        
        # Close the Qt browser UI
        if self.browser_ui:
            self.browser_ui.close()
            self.browser_ui = None
        
        # Close Playwright
        if self.playwright:
            await self.playwright.stop()
            self.playwright = None
    
    def run(self):
        """Run the application event loop."""
        return self.app.exec()
    
    def run_async(self, coro):
        """Run an async coroutine in the Qt event loop."""
        if not self.event_loop:
            self.event_loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self.event_loop)
        
        # Create a QTimer to process asyncio events
        timer = QTimer()
        timer.timeout.connect(lambda: self.event_loop.run_until_complete(asyncio.sleep(0)))
        timer.start(10)  # 10ms interval
        
        # Run the coroutine
        future = asyncio.run_coroutine_threadsafe(coro, self.event_loop)
        return future


class QPlaywrightBrowserContext:
    """
    A wrapper around a Playwright BrowserContext that provides both
    Playwright API and Qt UI integration.
    """
    def __init__(self, qbrowser, context, context_id):
        self.qbrowser = qbrowser
        self.context = context
        self.context_id = context_id
        self.pages = []
    
    async def new_page(self):
        """Create a new page in this context."""
        # Create a new page in the Playwright context
        pw_page = await self.context.new_page()
        
        # Create a new tab in the Qt browser
        qt_tab = self.qbrowser.browser_ui.new_page("about:blank")
        qt_tab.page = pw_page
        
        # Create a wrapper page
        page = QPlaywrightPage(pw_page, qt_tab)
        self.pages.append(page)
        
        return page
    
    async def close(self):
        """Close this context."""
        # Close all pages
        for page in self.pages:
            await page.close()
        
        # Close the Playwright context
        await self.context.close()
        
        # Remove from the qbrowser contexts
        if self.context_id in self.qbrowser.contexts:
            del self.qbrowser.contexts[self.context_id]


class QPlaywrightPage:
    """
    A wrapper around a Playwright Page that provides both
    Playwright API and Qt UI integration.
    """
    def __init__(self, pw_page, qt_tab):
        self.pw_page = pw_page
        self.qt_tab = qt_tab
        
        # Forward Playwright page methods and properties
        self.goto = pw_page.goto
        self.click = pw_page.click
        self.fill = pw_page.fill
        self.type = pw_page.type
        self.press = pw_page.press
        self.wait_for_selector = pw_page.wait_for_selector
        self.wait_for_navigation = pw_page.wait_for_navigation
        self.wait_for_load_state = pw_page.wait_for_load_state
        self.evaluate = pw_page.evaluate
        self.screenshot = pw_page.screenshot
        self.content = pw_page.content
        self.title = pw_page.title
        self.url = pw_page.url
    
    async def close(self):
        """Close this page."""
        # Close the Playwright page
        await self.pw_page.close()
        
        # Close the Qt tab
        # We need to use the Qt event loop for this
        index = self.qt_tab.parent.indexOf(self.qt_tab)
        if index >= 0:
            self.qt_tab.parent.close_tab(index)


