import aiohttp
import json
from datetime import datetime, timedelta
from . import BaseAdPlatform


class SnapchatAdsPlatform(BaseAdPlatform):
    """Snapchat Marketing API integration for paid ads."""

    BASE_URL = "https://adsapi.snapchat.com/v1"

    def _headers(self, account):
        return {
            "Authorization": f"Bearer {account.access_token}",
            "Content-Type": "application/json",
        }

    async def upload_video(self, video_url: str, account) -> str:
        """Upload video to Snapchat via Media Library."""
        url = f"{self.BASE_URL}/adaccounts/{account.account_id}/media"
        payload = {
            "media": [{
                "name": f"video_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}",
                "type": "VIDEO",
                "ad_account_id": account.account_id,
            }]
        }
        async with aiohttp.ClientSession() as session:
            async with session.post(url, json=payload, headers=self._headers(account)) as resp:
                result = await resp.json()
                media_list = result.get("media", [])
                if media_list:
                    media_id = media_list[0].get("media", {}).get("id")
                    # Upload the actual video file
                    upload_url = f"{self.BASE_URL}/media/{media_id}/upload"
                    async with session.get(video_url) as vresp:
                        video_data = await vresp.read()
                    form = aiohttp.FormData()
                    form.add_field("file", video_data, filename="video.mp4", content_type="video/mp4")
                    async with session.post(upload_url, data=form, headers={"Authorization": f"Bearer {account.access_token}"}) as uresp:
                        upload_result = await uresp.json()
                        if uresp.status in (200, 201):
                            return media_id
                        raise Exception(f"Snapchat video upload failed: {upload_result}")
                raise Exception(f"Snapchat media creation failed: {result}")

    async def create_campaign(self, ad_campaign, account) -> dict:
        """Create Snapchat ad: Campaign → Ad Squad → Ad Creative → Ad."""
        headers = self._headers(account)
        targeting = ad_campaign.targeting_json or {}
        ad_account_id = account.account_id

        async with aiohttp.ClientSession() as session:
            # 1. Create Campaign
            camp_url = f"{self.BASE_URL}/adaccounts/{ad_account_id}/campaigns"
            start_date = ad_campaign.start_date or datetime.utcnow()
            end_date = ad_campaign.end_date or (start_date + timedelta(days=ad_campaign.duration_days or 7))

            camp_payload = {
                "campaigns": [{
                    "name": ad_campaign.name,
                    "ad_account_id": ad_account_id,
                    "status": "PAUSED",
                    "start_time": start_date.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                    "end_time": end_date.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                    "daily_budget_micro": int(ad_campaign.daily_budget * 1_000_000),
                    "objective": "VIDEO_VIEWS",
                }]
            }
            async with session.post(camp_url, json=camp_payload, headers=headers) as resp:
                camp_result = await resp.json()
                campaigns = camp_result.get("campaigns", [])
                if not campaigns:
                    return {"status": "error", "error": f"Campaign creation failed: {camp_result}"}
                campaign_id = campaigns[0].get("campaign", {}).get("id")

            # 2. Upload video
            video_id = None
            if ad_campaign.campaign and ad_campaign.campaign.video_url:
                try:
                    video_id = await self.upload_video(ad_campaign.campaign.video_url, account)
                except Exception as e:
                    return {"status": "error", "error": f"Video upload failed: {e}"}

            # 3. Create Ad Creative
            creative_id = None
            if video_id:
                creative_url = f"{self.BASE_URL}/adaccounts/{ad_account_id}/creatives"
                creative_payload = {
                    "creatives": [{
                        "ad_account_id": ad_account_id,
                        "name": f"{ad_campaign.name} - Creative",
                        "type": "SNAP_AD",
                        "top_snap_media_id": video_id,
                        "headline": ad_campaign.ad_caption or ad_campaign.name,
                        "call_to_action": ad_campaign.ad_cta or "VIEW_MORE",
                    }]
                }
                async with session.post(creative_url, json=creative_payload, headers=headers) as resp:
                    creative_result = await resp.json()
                    creatives = creative_result.get("creatives", [])
                    if creatives:
                        creative_id = creatives[0].get("creative", {}).get("id")

            # 4. Create Ad Squad (targeting)
            squad_url = f"{self.BASE_URL}/campaigns/{campaign_id}/adsquads"
            geo_targets = []
            for loc in targeting.get("locations", ["SA"]):
                geo_targets.append({"country_code": loc})

            squad_targeting = {"geos": geo_targets}
            if targeting.get("age_min") or targeting.get("age_max"):
                squad_targeting["demographics"] = [{
                    "age_groups": self._map_age_to_snap(targeting.get("age_min", 18), targeting.get("age_max", 65))
                }]

            squad_payload = {
                "adsquads": [{
                    "name": f"{ad_campaign.name} - Ad Squad",
                    "campaign_id": campaign_id,
                    "type": "SNAP_ADS",
                    "status": "ACTIVE",
                    "billing_event": "IMPRESSION",
                    "bid_micro": 1000000,
                    "daily_budget_micro": int(ad_campaign.daily_budget * 1_000_000),
                    "start_time": start_date.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                    "end_time": end_date.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                    "targeting": squad_targeting,
                    "optimization_goal": "VIDEO_VIEWS",
                }]
            }
            async with session.post(squad_url, json=squad_payload, headers=headers) as resp:
                squad_result = await resp.json()
                squads = squad_result.get("adsquads", [])
                if not squads:
                    return {"status": "error", "error": f"Ad Squad creation failed: {squad_result}"}
                squad_id = squads[0].get("adsquad", {}).get("id")

            # 5. Create Ad
            ad_id = None
            if creative_id and squad_id:
                ad_url = f"{self.BASE_URL}/adsquads/{squad_id}/ads"
                ad_payload = {
                    "ads": [{
                        "name": f"{ad_campaign.name} - Ad",
                        "ad_squad_id": squad_id,
                        "creative_id": creative_id,
                        "status": "ACTIVE",
                        "type": "SNAP_AD",
                    }]
                }
                async with session.post(ad_url, json=ad_payload, headers=headers) as resp:
                    ad_result = await resp.json()
                    ads = ad_result.get("ads", [])
                    if ads:
                        ad_id = ads[0].get("ad", {}).get("id")

            # Activate campaign
            activate_url = f"{self.BASE_URL}/campaigns/{campaign_id}"
            await session.put(activate_url, json={"campaigns": [{"id": campaign_id, "status": "ACTIVE"}]}, headers=headers)

            return {
                "status": "active",
                "campaign_id": campaign_id,
                "squad_id": squad_id,
                "creative_id": creative_id,
                "ad_id": ad_id,
            }

    async def get_metrics(self, platform_campaign_id: str, account, date_from=None, date_to=None) -> list:
        """Fetch campaign stats from Snapchat."""
        if not date_from:
            date_from = (datetime.utcnow() - timedelta(days=7)).strftime("%Y-%m-%d")
        if not date_to:
            date_to = datetime.utcnow().strftime("%Y-%m-%d")

        url = f"{self.BASE_URL}/campaigns/{platform_campaign_id}/stats"
        params = {
            "granularity": "DAY",
            "fields": "impressions,swipes,video_views,spend,screen_time_millis",
            "start_time": f"{date_from}T00:00:00.000Z",
            "end_time": f"{date_to}T23:59:59.000Z",
        }
        metrics_list = []
        async with aiohttp.ClientSession() as session:
            async with session.get(url, params=params, headers=self._headers(account)) as resp:
                data = await resp.json()
                for ts in data.get("timeseries_stats", []):
                    for row in ts.get("timeseries", []):
                        stats = row.get("stats", {})
                        views = int(stats.get("video_views", 0))
                        clicks = int(stats.get("swipes", 0))
                        spend = int(stats.get("spend", 0)) / 1_000_000
                        metrics_list.append({
                            "date": row.get("start_time", "")[:10],
                            "impressions": int(stats.get("impressions", 0)),
                            "views": views,
                            "clicks": clicks,
                            "ctr": round((clicks / int(stats.get("impressions", 1))) * 100, 2) if int(stats.get("impressions", 0)) > 0 else 0,
                            "spend": spend,
                            "cost_per_view": round(spend / views, 4) if views > 0 else 0,
                            "cost_per_click": round(spend / clicks, 4) if clicks > 0 else 0,
                            "likes": 0,
                            "comments": 0,
                            "shares": 0,
                            "reach": 0,
                            "frequency": 0,
                        })
        return metrics_list

    async def pause_campaign(self, platform_campaign_id: str, account) -> dict:
        url = f"{self.BASE_URL}/campaigns/{platform_campaign_id}"
        payload = {"campaigns": [{"id": platform_campaign_id, "status": "PAUSED"}]}
        async with aiohttp.ClientSession() as session:
            async with session.put(url, json=payload, headers=self._headers(account)) as resp:
                return {"status": "paused"}

    async def resume_campaign(self, platform_campaign_id: str, account) -> dict:
        url = f"{self.BASE_URL}/campaigns/{platform_campaign_id}"
        payload = {"campaigns": [{"id": platform_campaign_id, "status": "ACTIVE"}]}
        async with aiohttp.ClientSession() as session:
            async with session.put(url, json=payload, headers=self._headers(account)) as resp:
                return {"status": "active"}

    async def delete_campaign(self, platform_campaign_id: str, account) -> dict:
        url = f"{self.BASE_URL}/campaigns/{platform_campaign_id}"
        async with aiohttp.ClientSession() as session:
            async with session.delete(url, headers=self._headers(account)) as resp:
                return {"status": "deleted"}

    @staticmethod
    def _map_age_to_snap(age_min, age_max):
        groups = []
        snap_ages = [
            ("13-17", 13, 17), ("18-20", 18, 20), ("21-24", 21, 24),
            ("25-34", 25, 34), ("35-49", 35, 49), ("50+", 50, 100),
        ]
        for code, lo, hi in snap_ages:
            if age_min <= hi and age_max >= lo:
                groups.append(code)
        return groups
