"""
Data Service for Instagram Data Fetching and Processing

This service handles all Instagram data operations:
1. Fetching account-level data (followers, media count)
2. Fetching account insights (profile views, growth)
3. Fetching audience demographics
4. Fetching media posts and their insights
5. Calculating aggregate metrics

Separation of Concerns:
- This service focuses ONLY on data fetching and calculation
- OAuth is handled by OAuthService
- AI/ML predictions are handled by NicheService
- Database operations are minimal (only what's needed for calculations)
"""

import logging
from typing import Dict, List, Optional, Tuple
from django.db.models import Avg, Sum

from ..instagram_api import (
    get_instagram_account_info,
    get_account_insights,
    get_user_media,
    get_media_insights,
    get_follower_demographics,
    get_audience_demographics,
    InstagramAPIError
)
from ..models import InfluencerProfile, MediaPost
from ..constants import MAX_MEDIA_POSTS_TO_FETCH, MediaType
from .data_helpers import (
    parse_media_insights,
    process_age_gender_follower_demographics,
    process_location_follower_demographics,
    process_reached_age_only,
    rebuild_audience_summary_from_structured,
)

logger = logging.getLogger(__name__)


class DataService:
    """
    Service class for handling Instagram data operations.

    This class encapsulates all data fetching and processing logic,
    making it reusable and testable.
    """

    @staticmethod
    def fetch_account_basic_info(profile: InfluencerProfile, access_token: str) -> Dict:
        """
        Fetch basic account information (followers, media count).

        Args:
            profile: InfluencerProfile instance
            access_token: Instagram access token

        Returns:
            dict: Account info with followers_count and media_count

        Updates:
            - profile.followers_count
            - profile.media_count
        """
        logger.info(f"Fetching basic account info for {profile.instagram_handle}")

        account_info = get_instagram_account_info(
            profile.instagram_business_id,
            access_token
        )

        profile.followers_count = account_info.get('followers_count', 0)
        profile.media_count = account_info.get('media_count', 0)
        profile.save()

        logger.info(f"Account info: {profile.followers_count} followers, {profile.media_count} posts")
        return account_info

    @staticmethod
    def fetch_account_insights(profile: InfluencerProfile, access_token: str) -> None:
        """
        Fetch account-level insights (profile views, follower growth).

        Args:
            profile: InfluencerProfile instance
            access_token: Instagram access token

        Updates:
            - profile.profile_views
            - profile.follower_growth
            - profile.follower_growth_rate_percent

        Flow:
            1. Fetch profile views (last 30 days total)
            2. Fetch follower count over time
            3. Calculate growth metrics
        """
        logger.info(f"Fetching account insights for {profile.instagram_handle}")

        # Fetch profile views
        try:
            profile_views_data = get_account_insights(
                profile.instagram_business_id,
                access_token,
                metrics=['profile_views'],
                period='day',
                metric_type='total_value'
            )

            total_views = 0
            for insight in profile_views_data.get('data', []):
                if insight.get('name') == 'profile_views':
                    total_views = insight.get('total_value', {}).get('value', 0)

            profile.profile_views = total_views or 0
            logger.info(f"Profile views: {profile.profile_views}")

        except Exception as e:
            logger.warning(f"Could not fetch profile views: {e}")
            profile.profile_views = 0

        # Fetch follower growth
        try:
            follower_data = get_account_insights(
                profile.instagram_business_id,
                access_token,
                metrics=['follower_count'],
                period='day'
            )

            for insight in follower_data.get('data', []):
                if insight.get('name') == 'follower_count':
                    values = insight.get('values', [])
                    if len(values) >= 2:
                        first_count = values[0].get('value', 0)
                        last_count = values[-1].get('value', 0)

                        profile.follower_growth = last_count - first_count

                        if first_count > 0:
                            profile.follower_growth_rate_percent = (
                                (profile.follower_growth / first_count) * 100
                            )

                        logger.info(f"Follower growth: {profile.follower_growth}")

        except Exception as e:
            logger.warning(f"Could not fetch follower growth: {e}")

        profile.save()

    @staticmethod
    def fetch_audience_demographics(profile: InfluencerProfile, access_token: str) -> None:
        """
        Fetch audience demographic data (age, gender, top countries/cities).

        Args:
            profile: InfluencerProfile instance
            access_token: Instagram access token

        Updates:
            - profile.female_audience_percentage / male_audience_percentage
            - profile.audience_age_distribution, audience_gender_distribution
            - profile.audience_top_countries, audience_top_cities
            - profile.audience_summary_string

        Note:
            If Instagram/Meta does not return follower breakdowns, demographic
            fields stay empty (or partial if reached-audience age is available)
            and audience_summary_string explains why.
        """
        logger.info(f"Fetching audience demographics for {profile.instagram_handle}")

        profile.audience_age_distribution = {}
        profile.audience_gender_distribution = {}
        profile.audience_top_countries = []
        profile.audience_top_cities = []

        real_ok = False
        try:
            demo_ag = get_follower_demographics(
                profile.instagram_business_id,
                access_token,
                breakdowns='age,gender',
            )
            real_ok = process_age_gender_follower_demographics(profile, demo_ag)
        except Exception as e:
            logger.warning(f"Could not fetch follower age/gender demographics: {e}")

        if real_ok:
            for breakdown, kind in (('country', 'country'), ('city', 'city')):
                try:
                    demo_loc = get_follower_demographics(
                        profile.instagram_business_id,
                        access_token,
                        breakdowns=breakdown,
                    )
                    process_location_follower_demographics(profile, demo_loc, kind)
                except Exception as e:
                    logger.info(
                        f"Follower demographics breakdown '{breakdown}' not available: {e}"
                    )

            rebuild_audience_summary_from_structured(profile)
        else:
            profile.female_audience_percentage = 0.0
            profile.male_audience_percentage = 0.0
            logger.info(
                "Follower age/gender demographics not returned by Instagram for %s",
                profile.instagram_handle,
            )

        # Best-effort: reached audience age if follower age is still empty
        if not profile.audience_age_distribution:
            try:
                reached_demo = get_audience_demographics(
                    profile.instagram_business_id,
                    access_token,
                    metric='reached_audience_demographics',
                    timeframe='this_month',
                    breakdowns='age',
                )
                process_reached_age_only(profile, reached_demo)
            except Exception as e:
                logger.info(f"Reached demographics not available: {e}")

        if not real_ok:
            if profile.audience_age_distribution:
                rebuild_audience_summary_from_structured(profile)
                prefix = (profile.audience_summary_string or "").strip()
                suffix = (
                    "Gender and location breakdowns are not available from Instagram "
                    "due to Meta audience size thresholds, API limitations, or missing data for this account."
                )
                profile.audience_summary_string = f"{prefix} {suffix}".strip() if prefix else suffix
            else:
                profile.audience_summary_string = (
                    "Audience demographics are not available from Instagram due to Meta minimum audience "
                    "requirements for age/gender/location breakdowns, API limitations, or the insights "
                    "request returning no data for this account."
                )

        profile.save()

    @staticmethod
    def fetch_media_posts(profile: InfluencerProfile, access_token: str) -> Tuple[int, int]:
        """
        Fetch media posts and their insights.

        Args:
            profile: InfluencerProfile instance
            access_token: Instagram access token

        Returns:
            Tuple of (posts_processed: int, posts_without_insights: int)
        """
        logger.info(f"Fetching media posts for {profile.instagram_handle}")

        media_list = get_user_media(
            profile.instagram_business_id,
            access_token,
            limit=MAX_MEDIA_POSTS_TO_FETCH
        )
        logger.info(f"Fetched {len(media_list)} media posts")

        # Delete old media posts
        MediaPost.objects.filter(influencer=profile).delete()

        posts_processed = 0
        posts_without_insights = 0

        for media in media_list:
            try:
                media_id = media.get('id')
                media_type = media.get('media_type', MediaType.IMAGE)
                caption = media.get('caption', '')
                timestamp = media.get('timestamp')

                # Fetch insights
                insights_data = get_media_insights(media_id, access_token, media_type)
                insights = parse_media_insights(insights_data)

                # Check if real data exists
                has_real_data = (
                    insights.get('impressions', 0) > 0 or
                    insights.get('reach', 0) > 0 or
                    insights.get('engagement', 0) > 0
                )

                if not has_real_data:
                    insights = {
                        'impressions': 0,
                        'reach': 0,
                        'engagement': 0,
                        'saved': 0,
                        'video_views': None,
                        'like_count': 0,
                        'comment_count': 0,
                    }
                    posts_without_insights += 1

                # Create MediaPost
                MediaPost.objects.create(
                    influencer=profile,
                    media_id=media_id,
                    media_type=media_type,
                    caption=caption,
                    timestamp=timestamp,
                    impressions=insights.get('impressions', 0),
                    reach=insights.get('reach', 0),
                    engagement=insights.get('engagement', 0),
                    like_count=media.get('like_count', insights.get('like_count', 0)),
                    comment_count=media.get('comments_count', insights.get('comment_count', 0)),
                    saved=insights.get('saved', 0),
                    video_views=insights.get('video_views')
                )

                posts_processed += 1

                if posts_processed % 10 == 0:
                    logger.info(f"Processed {posts_processed}/{len(media_list)} posts")

            except Exception as e:
                logger.error(f"Failed to process media {media.get('id')}: {e}")
                continue

        if posts_without_insights > 0:
            logger.info(
                "Post-level insights unavailable for %s/%s posts (stored as zero; "
                "Meta often omits impressions/reach for older posts or without sufficient permissions).",
                posts_without_insights,
                posts_processed,
            )

        return posts_processed, posts_without_insights

    @staticmethod
    def calculate_averages(profile: InfluencerProfile) -> None:
        """Calculate average metrics from all MediaPost records."""
        posts = MediaPost.objects.filter(influencer=profile)

        if posts.exists():
            aggregates = posts.aggregate(
                avg_impressions=Avg('impressions'),
                avg_reach=Avg('reach'),
                avg_engagement=Avg('engagement'),
                avg_likes=Avg('like_count'),
                avg_comments=Avg('comment_count'),
                avg_saved=Avg('saved')
            )

            profile.ave_impressions = aggregates['avg_impressions'] or 0
            profile.ave_reach = aggregates['avg_reach'] or 0
            profile.ave_engagement = aggregates['avg_engagement'] or 0
            profile.avg_likes = aggregates['avg_likes'] or 0
            profile.avg_comments = aggregates['avg_comments'] or 0
            profile.ave_saves = aggregates['avg_saved'] or 0

            # Video averages
            video_posts = posts.filter(media_type=MediaType.VIDEO, video_views__isnull=False)
            if video_posts.exists():
                profile.ave_video_views = video_posts.aggregate(avg=Avg('video_views'))['avg']

            # Engagement rate (average engagement per post / followers * 100)
            if profile.followers_count > 0:
                avg_engagement = aggregates['avg_engagement'] or 0
                profile.engagement_rate_percent = (avg_engagement / profile.followers_count) * 100

            profile.save()
            logger.info(f"Calculated averages - Impressions: {profile.ave_impressions:.0f}, "
                       f"Avg Likes: {profile.avg_likes:.1f}, Avg Comments: {profile.avg_comments:.1f}")

