import os
import sys
import tempfile
import subprocess
import random
import shutil

class BrowserNotFoundError(Exception):
    pass

class RenderError(Exception):
    pass

def _find_browser() -> str:
    if sys.platform == 'win32':
        paths = [
            r'C:\Program Files\Google\Chrome\Application\chrome.exe',
            r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
            r'C:\Program Files\Microsoft\Edge\Application\msedge.exe',
            r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
        ]
    elif sys.platform == 'darwin':
        paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
    else:
        paths = ['/usr/bin/google-chrome', '/usr/bin/chromium-browser', '/usr/bin/chromium']
        
    for p in paths:
        if os.path.exists(p):
            return p
    raise BrowserNotFoundError("No compatible browser (Chrome/Edge) found on system.")

class CoreImR:
    """
    Advanced configuration class for rendering HTML to images/documents.
    """
    def __init__(
        self, 
        width: int = 1280, 
        height: int = 720, 
        transparent: bool = False, 
        delay: int = 0, 
        browser_path: str = None,
        device_scale_factor: int = 1,
        timeout: int = 30,
        output_format: str = 'png',
        user_agent: str = None,
        disable_js: bool = False,
        dark_mode: bool = False,
        disable_web_security: bool = False
    ):
        """
        :param width: Viewport width (default 1280)
        :param height: Viewport height (default 720)
        :param transparent: Whether background is transparent (default False)
        :param delay: Milliseconds to wait before taking screenshot (default 0)
        :param browser_path: Custom path to Chrome/Edge executable
        :param device_scale_factor: To simulate high DPI / Retina (default 1)
        :param timeout: Maximum time in seconds to wait for the browser process to finish (default 30)
        :param output_format: Desired output format: 'png', 'pdf', 'jpeg', 'bmp', 'webp'
        :param user_agent: Override browser User-Agent string
        :param disable_js: Prevent JavaScript execution
        :param dark_mode: Force `prefers-color-scheme: dark` CSS rule
        :param disable_web_security: Bypass CORS for local file fetching
        """
        self.width = width
        self.height = height
        self.transparent = transparent
        self.delay = delay
        self.browser_path = browser_path or _find_browser()
        self.device_scale_factor = device_scale_factor
        self.timeout = timeout
        self.output_format = output_format.lower().replace("jpg", "jpeg")
        self.user_agent = user_agent
        self.disable_js = disable_js
        self.dark_mode = dark_mode
        self.disable_web_security = disable_web_security

    def render(self, html: str, output_path: str) -> bool:
        fd, tmp_html = tempfile.mkstemp(suffix=f'_coreimr_{random.randint(1000, 9999)}.html')
        with os.fdopen(fd, 'w', encoding='utf-8') as f:
            f.write(html)
            
        try:
            abs_output = os.path.abspath(output_path)
            
            needs_conversion = self.output_format not in ['png', 'pdf']
            native_output = abs_output if not needs_conversion else abs_output + ".tmp.png"

            cmd = [
                self.browser_path,
                '--headless',
                '--disable-gpu',
                '--hide-scrollbars',
                '--no-sandbox',
                '--disable-dev-shm-usage',
                f'--window-size={self.width},{self.height}',
            ]
            
            if self.output_format == 'pdf':
                cmd.append(f'--print-to-pdf={native_output}')
                cmd.append('--no-pdf-header-footer')
            else:
                cmd.append(f'--screenshot={native_output}')
                if self.transparent:
                    cmd.append('--default-background-color=00000000')

            # Extra rendering options packed into v1.1.1 Ultimate
            if self.delay > 0:
                cmd.append(f'--virtual-time-budget={self.delay}')
            if self.device_scale_factor > 1:
                cmd.append(f'--force-device-scale-factor={self.device_scale_factor}')
            if self.user_agent:
                cmd.append(f'--user-agent="{self.user_agent}"')
            if self.disable_js:
                cmd.append('--disable-javascript')
            if self.dark_mode:
                cmd.append('--force-dark-mode')
            if self.disable_web_security:
                cmd.append('--disable-web-security')
                cmd.append('--allow-file-access-from-files')
                
            cmd.append(f'file://{tmp_html}')
            
            result = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=self.timeout)
            if result.returncode != 0:
                raise RenderError(f"Browser command failed with exit code {result.returncode}")
                
            if needs_conversion:
                self._convert_image(native_output, abs_output, self.output_format)

            return True
        except subprocess.TimeoutExpired:
            raise RenderError(f"Browser rendering timed out after {self.timeout} seconds.")
        finally:
            try:
                os.remove(tmp_html)
            except Exception:
                pass

    def _convert_image(self, tmp_png: str, final_output: str, target_format: str):
        try:
            from PIL import Image
            img = Image.open(tmp_png)
            
            if target_format == 'jpeg' and img.mode in ('RGBA', 'LA'):
                background = Image.new('RGB', img.size, (255, 255, 255))
                background.paste(img, mask=img.split()[3]) 
                img = background
                
            img.save(final_output, format=target_format.upper())
            img.close()
            os.remove(tmp_png)
        except ImportError:
            # Fallback if Pillow is not installed
            shutil.copy(tmp_png, final_output)
            os.remove(tmp_png)
            print(f"⚠️ Warning: CoreImR saved '{final_output}' as a raw PNG because 'Pillow' is not installed.")
            print(f"⚠️ To natively convert formats to {target_format.upper()}, please run: pip install Pillow")
        except Exception as e:
            if os.path.exists(tmp_png):
                os.remove(tmp_png)
            raise RenderError(f"Failed to convert image to {target_format}: {str(e)}")


def render(html: str, output_path: str, **kwargs) -> bool:
    """
    Convenience function to render HTML string to an image file.
    Accepts kwargs: width, height, transparent, delay, browser_path, device_scale_factor, timeout, output_format, user_agent, disable_js, dark_mode, disable_web_security
    """
    instance = CoreImR(**kwargs)
    return instance.render(html, output_path)
