"""
Platform Discovery Module - Auto-discovers e-commerce platforms.

Discovers web URLs, Android packages, and iOS bundle IDs for e-commerce brands.
Public module - contains NO proprietary Baymard data.
"""
import asyncio
import logging
import re
from typing import Dict, List, Optional
from dataclasses import dataclass
import aiohttp
from bs4 import BeautifulSoup


logger = logging.getLogger(__name__)


@dataclass
class PlatformDiscoveryResult:
    """
    Result of platform discovery for an e-commerce brand.

    Attributes:
        brand_name: Brand name searched
        web_url: Discovered web URL (if found)
        android_package: Android package name (if found)
        ios_bundle_id: iOS bundle ID (if found)
        platforms_available: List of available platforms
        confidence: Confidence score 0.0-1.0
        metadata: Additional discovery metadata
    """
    brand_name: str
    web_url: Optional[str]
    android_package: Optional[str]
    ios_bundle_id: Optional[str]
    platforms_available: List[str]
    confidence: float
    metadata: Dict


class PlatformDiscovery:
    """
    Discovers all platforms (web, Android, iOS) for e-commerce brands.

    Uses multiple discovery methods:
    - Web search for official website
    - Google Play Store search for Android app
    - Apple App Store search for iOS app
    - Brand domain analysis
    """

    def __init__(self):
        """Initialize platform discovery."""
        self.session = None

    async def __aenter__(self):
        """Async context manager entry."""
        self.session = aiohttp.ClientSession()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Async context manager exit."""
        if self.session:
            await self.session.close()

    async def discover(self, brand_name: str) -> PlatformDiscoveryResult:
        """
        Discover all platforms for an e-commerce brand.

        Args:
            brand_name: Brand name to search (e.g., "Nike", "Target", "Temu")

        Returns:
            PlatformDiscoveryResult with discovered platforms
        """
        logger.info(f"🔍 Discovering platforms for: {brand_name}")

        # Ensure session exists
        if not self.session:
            self.session = aiohttp.ClientSession()

        metadata = {}
        platforms = []

        # Discover web URL
        web_url = await self._discover_web_url(brand_name)
        if web_url:
            platforms.append('web')
            metadata['web_discovery_method'] = 'domain_inference'

        # Discover Android package
        android_package = await self._discover_android_package(brand_name)
        if android_package:
            platforms.append('android')
            metadata['android_discovery_method'] = 'play_store_search'

        # Discover iOS bundle ID
        ios_bundle_id = await self._discover_ios_bundle_id(brand_name)
        if ios_bundle_id:
            platforms.append('ios')
            metadata['ios_discovery_method'] = 'app_store_search'

        # Calculate confidence
        confidence = len(platforms) / 3.0  # Max 3 platforms

        logger.info(
            f"✓ Discovered {len(platforms)} platforms: {', '.join(platforms)} "
            f"(confidence: {confidence:.2f})"
        )

        return PlatformDiscoveryResult(
            brand_name=brand_name,
            web_url=web_url,
            android_package=android_package,
            ios_bundle_id=ios_bundle_id,
            platforms_available=platforms,
            confidence=confidence,
            metadata=metadata
        )

    async def _discover_web_url(self, brand_name: str) -> Optional[str]:
        """
        Discover web URL for brand.

        Strategy:
        1. Infer from common patterns (brand.com)
        2. Search Google for official site
        3. Verify domain is e-commerce

        Args:
            brand_name: Brand name

        Returns:
            Web URL or None
        """
        try:
            # Strategy 1: Common domain patterns
            brand_slug = brand_name.lower().replace(' ', '')
            common_patterns = [
                f"https://www.{brand_slug}.com",
                f"https://{brand_slug}.com",
                f"https://shop.{brand_slug}.com",
            ]

            for url in common_patterns:
                if await self._verify_url_exists(url):
                    logger.debug(f"Found web URL via pattern: {url}")
                    return url

            # Strategy 2: Search-based discovery (placeholder)
            # Would use search API in production
            logger.debug(f"Web URL not found via common patterns for {brand_name}")
            return None

        except Exception as e:
            logger.warning(f"Error discovering web URL: {e}")
            return None

    async def _discover_android_package(self, brand_name: str) -> Optional[str]:
        """
        Discover Android package name.

        Strategy:
        1. Live search via google-play-scraper (if installed)
        2. Fall back to known_packages table

        Args:
            brand_name: Brand name

        Returns:
            Android package name or None
        """
        # Known brand table (fallback cache)
        known_packages = {
            'amazon': 'com.amazon.mShop.android.shopping',
            'nike': 'com.nike.omega',
            'target': 'com.target.ui',
            'walmart': 'com.walmart.android',
            'temu': 'com.einnovation.temu',
            'wish': 'com.contextlogic.wish',
            'shein': 'com.zzkko',
            'ebay': 'com.ebay.mobile',
            'etsy': 'com.etsy.android',
            'aliexpress': 'com.alibaba.aliexpresshd',
        }

        brand_slug = brand_name.lower().replace(' ', '')

        # Strategy 1: google-play-scraper (live)
        try:
            from google_play_scraper import search as gps_search
            results = gps_search(brand_name, n_hits=3, lang='en', country='us')
            if results:
                package = results[0]['appId']
                logger.debug(f"Found Android package via Play Store search: {package}")
                return package
        except ImportError:
            logger.debug("google-play-scraper not installed; using known_packages table")
        except Exception as exc:
            logger.debug(f"Play Store search failed: {exc}")

        # Strategy 2: known table fallback
        if brand_slug in known_packages:
            package = known_packages[brand_slug]
            logger.debug(f"Found Android package from known list: {package}")
            return package

        logger.debug(f"Android package not found for {brand_name}")
        return None

    async def _discover_ios_bundle_id(self, brand_name: str) -> Optional[str]:
        """
        Discover iOS bundle ID.

        Strategy:
        1. Live search via iTunes Search API (public, no key required)
        2. Fall back to known_bundles table

        Args:
            brand_name: Brand name

        Returns:
            iOS bundle ID or None
        """
        # Known brand table (fallback cache)
        known_bundles = {
            'amazon': 'com.amazon.Amazon',
            'nike': 'com.nike.onenikecommerce',
            'target': 'com.target.mobile',
            'walmart': 'com.walmart.customer',
            'temu': 'com.temu.app',
            'wish': 'com.contextlogic.wish',
            'shein': 'com.zzkko.shein',
            'ebay': 'com.ebay.iphone',
            'etsy': 'com.etsy.etsyforios',
            'aliexpress': 'com.alibaba.aliexpress',
        }

        brand_slug = brand_name.lower().replace(' ', '')

        # Strategy 1: iTunes Search API (live, public, no auth required)
        try:
            import urllib.parse
            term = urllib.parse.quote(brand_name)
            url = (
                f"https://itunes.apple.com/search"
                f"?term={term}&entity=software&country=us&limit=5"
            )
            async with self.session.get(url, timeout=8) as resp:
                if resp.status == 200:
                    data = await resp.json(content_type=None)
                    results = data.get("results", [])
                    if results:
                        bundle = results[0].get("bundleId")
                        if bundle:
                            logger.debug(f"Found iOS bundle ID via iTunes API: {bundle}")
                            return bundle
        except Exception as exc:
            logger.debug(f"iTunes Search API failed: {exc}")

        # Strategy 2: known table fallback
        if brand_slug in known_bundles:
            bundle = known_bundles[brand_slug]
            logger.debug(f"Found iOS bundle ID from known list: {bundle}")
            return bundle

        logger.debug(f"iOS bundle ID not found for {brand_name}")
        return None

    async def _verify_url_exists(self, url: str) -> bool:
        """
        Verify URL exists and is accessible.

        Args:
            url: URL to verify

        Returns:
            True if URL exists and returns 200
        """
        try:
            async with self.session.head(url, timeout=5, allow_redirects=True) as response:
                return response.status == 200
        except Exception:
            return False

    async def discover_batch(self, brand_names: List[str]) -> Dict[str, PlatformDiscoveryResult]:
        """
        Discover platforms for multiple brands in parallel.

        Args:
            brand_names: List of brand names to discover

        Returns:
            Dict mapping brand name to discovery result
        """
        logger.info(f"🔍 Discovering platforms for {len(brand_names)} brands in parallel")

        # Create tasks for parallel discovery
        tasks = [self.discover(brand) for brand in brand_names]
        results = await asyncio.gather(*tasks, return_exceptions=True)

        # Build results dict
        results_dict = {}
        for brand, result in zip(brand_names, results):
            if isinstance(result, Exception):
                logger.error(f"Error discovering {brand}: {result}")
                results_dict[brand] = PlatformDiscoveryResult(
                    brand_name=brand,
                    web_url=None,
                    android_package=None,
                    ios_bundle_id=None,
                    platforms_available=[],
                    confidence=0.0,
                    metadata={'error': str(result)}
                )
            else:
                results_dict[brand] = result

        logger.info(f"✓ Completed batch discovery for {len(brand_names)} brands")
        return results_dict


class PlayStoreAPI:
    """
    Google Play Store API integration (placeholder).

    In production, would integrate with:
    - Google Play Developer API
    - SerpAPI for Play Store search
    - Custom scraping (if necessary)
    """

    async def search_app(self, query: str) -> Optional[Dict]:
        """
        Search Google Play Store for app.

        Args:
            query: Search query (brand name)

        Returns:
            App metadata including package name, or None
        """
        # Placeholder - would implement actual API call
        logger.debug(f"PlayStoreAPI.search_app: {query} (not implemented)")
        return None


class AppStoreAPI:
    """
    Apple App Store API integration (placeholder).

    In production, would integrate with:
    - iTunes Search API
    - App Store Connect API
    - Custom scraping (if necessary)
    """

    async def search_app(self, query: str) -> Optional[Dict]:
        """
        Search Apple App Store for app.

        Args:
            query: Search query (brand name)

        Returns:
            App metadata including bundle ID, or None
        """
        # Placeholder - would implement actual API call
        logger.debug(f"AppStoreAPI.search_app: {query} (not implemented)")
        return None


# Convenience function
async def discover_platforms(brand_name: str) -> PlatformDiscoveryResult:
    """
    Convenience function to discover platforms for a brand.

    Args:
        brand_name: Brand name to search

    Returns:
        PlatformDiscoveryResult
    """
    async with PlatformDiscovery() as discovery:
        return await discovery.discover(brand_name)
