"""
Unified API client for Coomer.party and Kemono.party
Both services share the same API structure (Kemono fork)
"""

import aiohttp
import asyncio
from typing import List, Optional, Dict, Any

from modules.base_module import LoggingMixin, RateLimitMixin
from .models import Creator, Post, Attachment


class PaidContentAPIClient(LoggingMixin, RateLimitMixin):
    """
    API client for Coomer and Kemono archival services

    API Endpoints:
    - GET /creators - List all creators
    - GET /{service}/user/{creator_id} - Get creator info
    - GET /{service}/user/{creator_id} - Get creator's posts (paginated with ?o=offset)
    - GET /{service}/user/{creator_id}/post/{post_id} - Get single post
    """

    # Fallback URLs if database doesn't have them configured
    DEFAULT_SERVICE_URLS = {
        'coomer': 'https://coomer.party',
        'kemono': 'https://kemono.party'
    }

    SUPPORTED_PLATFORMS = {
        'coomer': ['onlyfans', 'fansly', 'candfans'],
        'kemono': ['patreon', 'fanbox', 'gumroad', 'subscribestar', 'discord']
    }

    def __init__(self, service_id: str, session_cookie: str = None, base_url: str = None, log_callback=None):
        self._init_logger('PaidContent', log_callback, default_module='API')
        self._init_rate_limiter(min_delay=0.5, max_delay=2.0, batch_delay_min=1, batch_delay_max=3)

        self.service_id = service_id

        # Use provided base_url, or fall back to defaults
        if base_url:
            # If base_url includes /api/v1, extract just the base
            if '/api/v1' in base_url:
                self.base_url = base_url.replace('/api/v1', '').rstrip('/')
            else:
                self.base_url = base_url.rstrip('/')
        else:
            self.base_url = self.DEFAULT_SERVICE_URLS.get(service_id)

        self.api_url = f"{self.base_url}/api/v1"
        self.session_cookie = session_cookie
        self._session: Optional[aiohttp.ClientSession] = None

    async def _get_session(self) -> aiohttp.ClientSession:
        """Get or create aiohttp session"""
        if self._session is None or self._session.closed:
            # Note: Coomer/Kemono require 'Accept: text/css' header as anti-scraping measure
            # Despite this, they still return JSON responses
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                'Accept': 'text/css',
                'Accept-Encoding': 'gzip, deflate, br',
                'Referer': self.base_url
            }
            cookies = {}
            if self.session_cookie:
                cookies['session'] = self.session_cookie

            timeout = aiohttp.ClientTimeout(total=30)
            self._session = aiohttp.ClientSession(headers=headers, cookies=cookies, timeout=timeout)
        return self._session

    async def close(self):
        """Close the aiohttp session"""
        if self._session and not self._session.closed:
            await self._session.close()
            self._session = None

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()

    async def check_health(self) -> Dict[str, Any]:
        """Check API health status"""
        import time
        try:
            session = await self._get_session()
            start = time.time()
            async with session.get(f"{self.api_url}/creators", timeout=aiohttp.ClientTimeout(total=10)) as resp:
                elapsed = time.time() - start
                if resp.status == 200:
                    # content_type=None allows parsing JSON regardless of response content-type
                    await resp.json(content_type=None)
                    return {'status': 'healthy', 'response_time': round(elapsed, 3)}
                elif resp.status == 429:
                    return {'status': 'rate_limited', 'response_code': 429}
                else:
                    return {'status': 'degraded', 'response_code': resp.status}
        except asyncio.TimeoutError:
            return {'status': 'timeout', 'error': 'Request timed out'}
        except Exception as e:
            return {'status': 'down', 'error': str(e)}

    async def get_all_creators(self) -> List[Dict]:
        """Get list of all available creators (for search)"""
        self._delay_between_items()
        try:
            session = await self._get_session()
            async with session.get(f"{self.api_url}/creators") as resp:
                if resp.status == 200:
                    return await resp.json(content_type=None)
                self.log(f"Failed to get creators list: HTTP {resp.status}", 'warning')
                return []
        except Exception as e:
            self.log(f"Error getting creators list: {e}", 'error')
            return []

    async def get_creator(self, platform: str, creator_id: str) -> Optional[Creator]:
        """Get creator info"""
        self._delay_between_items()
        try:
            session = await self._get_session()

            # First try to get creator profile
            url = f"{self.api_url}/{platform}/user/{creator_id}/profile"
            async with session.get(url) as resp:
                if resp.status == 200:
                    data = await resp.json(content_type=None)
                    return Creator.from_api(data, self.service_id, platform, self.base_url)

            # Fallback: get first post to extract creator info
            url = f"{self.api_url}/{platform}/user/{creator_id}/posts"
            async with session.get(url) as resp:
                if resp.status == 200:
                    posts = await resp.json(content_type=None)
                    if posts and len(posts) > 0:
                        # Extract creator info from first post
                        first_post = posts[0]
                        # Construct image URLs - use .st instead of .party
                        from urllib.parse import urlparse
                        parsed = urlparse(self.base_url)
                        # Convert .party to .st for image URLs (coomer.party/kemono.party images are at .st)
                        netloc = parsed.netloc.replace('.party', '.st')
                        img_domain = f"img.{netloc}"
                        profile_image_url = f"https://{img_domain}/icons/{platform}/{creator_id}"
                        banner_image_url = f"https://{img_domain}/banners/{platform}/{creator_id}"
                        return Creator(
                            creator_id=creator_id,
                            service_id=self.service_id,
                            platform=platform,
                            username=first_post.get('user', creator_id),
                            display_name=first_post.get('user', creator_id),
                            profile_image_url=profile_image_url,
                            banner_image_url=banner_image_url
                        )

            self.log(f"Creator not found: {platform}/{creator_id}", 'warning')
            return None

        except Exception as e:
            self.log(f"Error getting creator {platform}/{creator_id}: {e}", 'error')
            return None

    async def get_creator_posts(self, platform: str, creator_id: str, offset: int = 0) -> List[Post]:
        """Get creator's posts (50 per page by default)"""
        self._delay_between_items()
        try:
            session = await self._get_session()

            url = f"{self.api_url}/{platform}/user/{creator_id}/posts"
            params = {'o': offset} if offset > 0 else {}

            async with session.get(url, params=params) as resp:
                if resp.status == 200:
                    data = await resp.json(content_type=None)
                    return [Post.from_api(p, self.service_id, platform, creator_id, self.base_url) for p in data]
                elif resp.status == 404:
                    self.log(f"Creator not found: {platform}/{creator_id}", 'warning')
                else:
                    self.log(f"Failed to get posts: HTTP {resp.status}", 'warning')
                return []

        except Exception as e:
            self.log(f"Error getting posts for {platform}/{creator_id}: {e}", 'error')
            return []

    async def get_all_creator_posts(self, platform: str, creator_id: str,
                                     since_date: str = None, max_posts: int = None,
                                     progress_callback=None) -> List[Post]:
        """Fetch all posts with pagination"""
        all_posts = []
        offset = 0
        page = 0

        self.log(f"Fetching posts for {platform}/{creator_id}", 'info')

        while True:
            posts = await self.get_creator_posts(platform, creator_id, offset)
            if not posts:
                break

            for post in posts:
                # Stop if we've reached posts we've already seen
                if since_date and post.published_at and post.published_at <= since_date:
                    self.log(f"Reached already-seen post date: {post.published_at}", 'debug')
                    return all_posts

                all_posts.append(post)

                if max_posts and len(all_posts) >= max_posts:
                    self.log(f"Reached max posts limit: {max_posts}", 'debug')
                    return all_posts

            page += 1
            offset += 50

            if progress_callback:
                progress_callback(page, len(all_posts))

            self._delay_between_batches()

        self.log(f"Fetched {len(all_posts)} posts for {platform}/{creator_id}", 'info')
        return all_posts

    async def get_post(self, platform: str, creator_id: str, post_id: str) -> Optional[Post]:
        """Get single post by ID"""
        self._delay_between_items()
        try:
            session = await self._get_session()

            url = f"{self.api_url}/{platform}/user/{creator_id}/post/{post_id}"
            async with session.get(url) as resp:
                if resp.status == 200:
                    data = await resp.json(content_type=None)
                    # Single post endpoint wraps response in {"post": {...}}
                    if isinstance(data, dict) and 'post' in data:
                        data = data['post']
                    return Post.from_api(data, self.service_id, platform, creator_id, self.base_url)
                return None

        except Exception as e:
            self.log(f"Error getting post {post_id}: {e}", 'error')
            return None

    async def search_creators(self, query: str, platform: str = None) -> List[Dict]:
        """Search for creators by name"""
        self._delay_between_items()
        try:
            # Get all creators and filter locally (API doesn't have search endpoint)
            all_creators = await self.get_all_creators()

            query_lower = query.lower()
            results = []

            for creator in all_creators:
                if platform and creator.get('service') != platform:
                    continue

                name = (creator.get('name') or '').lower()
                if query_lower in name:
                    results.append({
                        'id': creator.get('id'),
                        'name': creator.get('name'),
                        'service': creator.get('service'),
                        'indexed': creator.get('indexed'),
                        'updated': creator.get('updated'),
                        'favorited': creator.get('favorited', 0)
                    })

            # Sort by favorited count (popularity)
            results.sort(key=lambda x: x.get('favorited', 0), reverse=True)
            return results[:50]  # Limit results

        except Exception as e:
            self.log(f"Error searching creators: {e}", 'error')
            return []

    def get_attachment_url(self, server_path: str) -> str:
        """Convert server path to full download URL"""
        if not server_path:
            return ''
        if server_path.startswith('http'):
            return server_path
        return f"{self.base_url}/data{server_path}"

    def get_thumbnail_url(self, server_path: str) -> str:
        """Get thumbnail URL for an attachment"""
        if not server_path:
            return ''
        if server_path.startswith('http'):
            return server_path
        return f"{self.base_url}/thumbnail/data{server_path}"

    @classmethod
    def get_supported_platforms(cls, service_id: str) -> List[str]:
        """Get list of supported platforms for a service"""
        return cls.SUPPORTED_PLATFORMS.get(service_id, [])

    @classmethod
    def is_valid_service(cls, service_id: str) -> bool:
        """Check if service ID is valid"""
        return service_id in cls.SERVICE_URLS

    @classmethod
    def get_service_ids(cls) -> List[str]:
        """Get list of all service IDs"""
        return list(cls.SERVICE_URLS.keys())
