"""
Brand - Extract and manage brand assets for slide decks.
"""
from dataclasses import dataclass, field
from typing import Optional, Dict
import asyncio
import logging
import re
from urllib.parse import urljoin, urlparse

logger = logging.getLogger(__name__)

try:
    import aiohttp
    HAS_AIOHTTP = True
except ImportError:
    HAS_AIOHTTP = False


HEX_COLOR_PATTERN = re.compile(r'^#(?:[0-9a-fA-F]{3}){1,2}$')


def _validate_hex_color(value: str, field_name: str) -> None:
    """Validate hex color format."""
    if not HEX_COLOR_PATTERN.match(value):
        raise ValueError(
            f"{field_name} must be a valid hex color (e.g., '#054dfe' or '#fff'), "
            f"got '{value}'"
        )


@dataclass
class Brand:
    """Brand assets for consistent slide styling."""

    # Colors
    primary: str = "#054dfe"        # Main accent color
    secondary: str = "#15aebf"      # Secondary accent
    background: str = "#ffffff"     # Slide background
    surface: str = "#f8f8f8"        # Cards, elevated surfaces
    text: str = "#191919"           # Primary text
    muted: str = "#6b6b6b"          # Secondary/muted text

    # Typography
    font_headline: str = "Inter"    # Headlines, titles
    font_body: str = "Inter"        # Body text

    # Logo
    logo_svg: Optional[str] = None
    logo_url: Optional[str] = None

    # Metadata
    name: str = ""
    domain: str = ""

    def __post_init__(self):
        """Validate brand fields after initialization."""
        color_fields = ['primary', 'secondary', 'background', 'surface', 'text', 'muted']
        for field_name in color_fields:
            value = getattr(self, field_name)
            _validate_hex_color(value, field_name)

    @property
    def clearbit_logo_url(self) -> str:
        """Get logo URL from Clearbit (free, no auth required)."""
        domain = self.domain or "example.com"
        # Remove www. prefix if present
        if domain.startswith("www."):
            domain = domain[4:]
        return f"https://logo.clearbit.com/{domain}"

    def logo_img(self, size: int = 40, style: str = "") -> str:
        """
        Generate an img tag for the logo.

        Uses in order: logo_svg, logo_url, clearbit_logo_url

        Args:
            size: Logo size in pixels
            style: Additional inline styles

        Returns:
            HTML img tag or inline SVG
        """
        if self.logo_svg:
            # Inject size into SVG
            svg = self.logo_svg
            if 'width=' not in svg.lower():
                svg = svg.replace('<svg', f'<svg width="{size}" height="{size}"', 1)
            return svg

        url = self.logo_url or self.clearbit_logo_url
        return f'<img src="{url}" alt="{self.name} logo" style="height:{size}px; {style}" />'

    def text_logo(self, size: int = 28, weight: int = 700) -> str:
        """
        Generate a text-based logo using brand font.

        Args:
            size: Font size in pixels
            weight: Font weight

        Returns:
            HTML span with styled brand name
        """
        name = self.name or self.domain.split('.')[0]
        return f'<span style="font-family:\'{self.font_headline}\',sans-serif; font-size:{size}px; font-weight:{weight}; color:{self.text};">{name}</span>'

    @property
    def google_fonts_url(self) -> str:
        """Generate Google Fonts URL for brand fonts."""
        fonts = set([self.font_headline, self.font_body])
        font_params = []
        for font in fonts:
            # Convert "Space Grotesk" to "Space+Grotesk"
            font_name = font.replace(" ", "+")
            font_params.append(f"family={font_name}:wght@400;500;600;700;800")
        return f"https://fonts.googleapis.com/css2?{'&'.join(font_params)}&display=swap"

    @classmethod
    def from_domain(cls, domain: str) -> "Brand":
        """
        Extract brand assets from a website domain.

        Args:
            domain: Website domain (e.g., "scaile.tech" or "https://scaile.tech")

        Returns:
            Brand with extracted colors, fonts, and logo
        """
        if not HAS_AIOHTTP:
            raise ImportError("aiohttp required for brand extraction. Install with: pip install aiohttp")

        return asyncio.run(_extract_brand(domain))


async def _extract_brand(url: str) -> Brand:
    """Extract brand assets from URL."""
    # Normalize URL
    if not url.startswith(('http://', 'https://')):
        url = f'https://{url}'

    parsed = urlparse(url)
    domain = parsed.netloc or parsed.path.split('/')[0]

    brand = Brand(
        domain=domain,
        name=domain.split('.')[0].title()
    )

    async with aiohttp.ClientSession() as session:
        html = await _fetch_html(session, url)
        if not html:
            return brand

        # Extract components
        brand.logo_url, brand.logo_svg = await _extract_logo(session, url, html)
        colors = _extract_colors(html)
        fonts = _extract_fonts(html)

        # Apply extracted values
        if colors.get('accent'):
            brand.primary = colors['accent']
        if colors.get('background'):
            brand.background = colors['background']
        if colors.get('text'):
            brand.text = colors['text']
        if fonts.get('headline'):
            brand.font_headline = fonts['headline']
        if fonts.get('body'):
            brand.font_body = fonts['body']

    return brand


async def _fetch_html(session, url: str) -> Optional[str]:
    """Fetch HTML content from URL."""
    try:
        headers = {'User-Agent': 'Mozilla/5.0 (compatible; deckforge/1.0)'}
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=15), headers=headers) as resp:
            if resp.status == 200:
                return await resp.text()
            logger.warning(f"Failed to fetch {url}: HTTP {resp.status}")
    except Exception as e:
        logger.warning(f"Failed to fetch {url}: {e}")
    return None


async def _extract_logo(session, base_url: str, html: str):
    """Extract logo URL and/or SVG content."""
    logo_url = None
    logo_svg = None

    # Look for logo images
    patterns = [
        r'<img[^>]*(?:src|alt|class)=["\'][^"\']*logo[^"\']*["\'][^>]*src=["\']([^"\']+)["\']',
        r'<img[^>]*src=["\']([^"\']+)["\'][^>]*(?:alt|class)=["\'][^"\']*logo[^"\']*["\']',
        r'<img[^>]*src=["\']([^"\']*logo[^"\']*)["\']',
    ]

    for pattern in patterns:
        match = re.search(pattern, html, re.IGNORECASE)
        if match:
            logo_url = urljoin(base_url, match.group(1))
            break

    # Look for SVG logo
    svg_links = re.findall(r'(?:src|href)=["\']([^"\']+\.svg(?:\?[^"\']*)?)["\']', html, re.IGNORECASE)
    for svg_link in svg_links[:5]:
        if 'logo' in svg_link.lower():
            full_url = urljoin(base_url, svg_link)
            try:
                async with session.get(full_url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
                    if resp.status == 200:
                        content = await resp.text()
                        match = re.search(r'<svg[^>]*>.*?</svg>', content, re.DOTALL | re.IGNORECASE)
                        if match and len(match.group(0)) > 50:
                            logo_svg = match.group(0)
                            logo_url = full_url
                            break
            except Exception as e:
                logger.debug(f"Failed to fetch SVG {full_url}: {e}")

    return logo_url, logo_svg


def _extract_colors(html: str) -> Dict[str, str]:
    """Extract color palette from CSS."""
    colors = {}

    # Find hex colors
    hex_colors = re.findall(r'#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b', html)

    # Find rgb colors
    rgb_colors = re.findall(r'rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)', html)
    for r, g, b in rgb_colors:
        hex_colors.append(f'#{int(r):02x}{int(g):02x}{int(b):02x}')

    # Count occurrences
    color_counts = {}
    for color in hex_colors:
        color = color.lower()
        if len(color) == 4:
            color = f'#{color[1]*2}{color[2]*2}{color[3]*2}'
        color_counts[color] = color_counts.get(color, 0) + 1

    # Categorize by brightness/saturation
    sorted_colors = sorted(color_counts.items(), key=lambda x: -x[1])

    for color, _ in sorted_colors[:15]:
        r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)
        brightness = (r * 299 + g * 587 + b * 114) / 1000

        if brightness < 50 and 'text' not in colors:
            colors['text'] = color
        elif brightness > 240 and 'background' not in colors:
            colors['background'] = color
        elif not colors.get('accent'):
            max_c, min_c = max(r, g, b), min(r, g, b)
            saturation = (max_c - min_c) / max_c if max_c > 0 else 0
            if saturation > 0.4 and 50 < brightness < 200:
                colors['accent'] = color

    return colors


def _extract_fonts(html: str) -> Dict[str, str]:
    """Extract font families."""
    fonts = {}

    # Google Fonts
    gf_match = re.search(r'href=["\']([^"\']*fonts\.googleapis\.com[^"\']+)["\']', html)
    if gf_match:
        url = gf_match.group(1)
        font_matches = re.findall(r'family=([^:&]+)', url)
        if font_matches:
            fonts['headline'] = font_matches[0].replace('+', ' ')
            if len(font_matches) > 1:
                fonts['body'] = font_matches[1].replace('+', ' ')

    # CSS font-family
    ff_matches = re.findall(r'font-family:\s*["\']?([^"\'};,]+)', html)
    for ff in ff_matches:
        ff = ff.strip().split(',')[0].strip("'\"")
        if ff and ff not in ['inherit', 'sans-serif', 'serif', 'monospace', '-apple-system']:
            if 'headline' not in fonts:
                fonts['headline'] = ff
            elif 'body' not in fonts and ff != fonts.get('headline'):
                fonts['body'] = ff

    return fonts
