#!/usr/bin/env python3
"""
Social Media Batch Scheduler - Pinterest, Instagram, Facebook
Runs Monday morning, schedules posts for all three platforms:
- Pinterest: 3 listings/day at 9 AM, 2 PM, 7 PM (21 pins/day)
- Instagram: 1 post/day at 12 PM noon (2 PM Pinterest listing)
- Facebook: 1 post/day at 10 AM (9 AM Pinterest listing) - weekdays only
"""

import json
import sys
import requests
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Optional, List, Dict
import logging
import time

# Add parent to path for config
sys.path.insert(0, str(Path(__file__).parent))
from config import *

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(LOG_DIR / "social-batch.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# Scheduling constants
LISTINGS_PER_DAY = 3
DAYS_TO_SCHEDULE = 7
PINTEREST_TIME_SLOTS = ["09:00", "14:00", "19:00"]
FACEBOOK_TIME_SLOT = "10:00"  # Weekdays only
INSTAGRAM_TIME_SLOT = "12:00"  # All days
MAX_PINS_PER_DAY = 25


def get_headers() -> dict:
    """Get Publer API headers"""
    return {
        "Authorization": f"Bearer-API {PUBLER_API_KEY}",
        "Publer-Workspace-Id": PUBLER_WORKSPACE_ID,
        "Content-Type": "application/json"
    }


def load_listings() -> dict:
    """Load listings JSON"""
    if not LISTINGS_JSON.exists():
        logger.error(f"Listings file not found: {LISTINGS_JSON}")
        sys.exit(1)
    
    with open(LISTINGS_JSON, 'r') as f:
        return json.load(f)


def save_listings(data: dict):
    """Save listings JSON"""
    with open(LISTINGS_JSON, 'w') as f:
        json.dump(data, f, indent=2)


def find_archive_folder(listing: dict) -> Optional[Path]:
    """Find archive folder matching a listing by title keywords."""
    title = listing.get("title", "").lower()
    
    exclude_folders = ["listed", "rejected", "source-pngs", "cleanup-20260210"]
    archive_folders = [
        f for f in ARCHIVE_DIR.iterdir() 
        if f.is_dir() and f.name not in exclude_folders
    ]
    
    title_words = []
    for word in title.lower().replace(",", "").replace("-", " ").split():
        if len(word) > 3 and word not in ["with", "for", "the", "and", "svg", "cut", "file", "cricut", "silhouette"]:
            title_words.append(word)
    title_words = title_words[:4]
    
    best_match = None
    best_score = 0
    
    for folder in archive_folders:
        folder_name = folder.name.lower().replace("_", " ").replace("-", " ")
        score = sum(1 for w in title_words if w in folder_name)
        if score > best_score:
            best_score = score
            best_match = folder
    
    if best_score >= 2:
        return best_match
    return None


def get_images_to_post(folder: Path) -> list[Path]:
    """Get all JPGs from folder, excluding whats-included"""
    images = []
    for img in folder.glob("*.jpg"):
        if img.name.lower() not in [e.lower() for e in EXCLUDED_FILES]:
            images.append(img)
    return sorted(images)


def get_unposted_listings(data: dict, max_count: int = 35) -> List[dict]:
    """Get unposted listings, priority first, up to max_count."""
    listings = data.get("listings", [])
    unposted = []
    
    # Priority listings first
    for listing in listings:
        if len(unposted) >= max_count:
            break
        if listing.get("priority", False) and not listing.get("delisted"):
            posted = set(listing.get("posted_images", []))
            scheduled = set(listing.get("scheduled_images", []))
            folder = find_archive_folder(listing)
            if folder:
                images = get_images_to_post(folder)
                remaining = [img for img in images if img.name not in posted and img.name not in scheduled]
                if remaining:
                    unposted.append(listing)
    
    # Then non-priority listings
    for listing in listings:
        if len(unposted) >= max_count:
            break
        if not listing.get("priority", False) and not listing.get("delisted"):
            posted = set(listing.get("posted_images", []))
            scheduled = set(listing.get("scheduled_images", []))
            folder = find_archive_folder(listing)
            if folder:
                images = get_images_to_post(folder)
                remaining = [img for img in images if img.name not in posted and img.name not in scheduled]
                if remaining:
                    unposted.append(listing)
    
    return unposted


def upload_media(image_path: Path) -> Optional[dict]:
    """Upload image to Publer Media Library."""
    headers = {
        "Authorization": f"Bearer-API {PUBLER_API_KEY}",
        "Publer-Workspace-Id": PUBLER_WORKSPACE_ID
    }
    
    try:
        with open(image_path, 'rb') as f:
            files = {'file': (image_path.name, f, 'image/jpeg')}
            resp = requests.post(
                f"{PUBLER_API_BASE}/media",
                headers=headers,
                files=files,
                data={'direct_upload': 'true', 'in_library': 'false'}
            )
            resp.raise_for_status()
            result = resp.json()
            return {
                "id": result.get("id") or result.get("_id"),
                "path": result.get("path", ""),
                "thumbnail": result.get("thumbnail", "")
            }
    except Exception as e:
        logger.error(f"Failed to upload media {image_path.name}: {e}")
        return None


def poll_job_status(job_id: str, max_attempts: int = 30) -> Optional[dict]:
    """Poll job status until completed or failed."""
    headers = get_headers()
    
    for attempt in range(max_attempts):
        try:
            resp = requests.get(
                f"{PUBLER_API_BASE}/job_status/{job_id}",
                headers=headers
            )
            resp.raise_for_status()
            result = resp.json()
            
            if isinstance(result, list):
                result = result[0] if result else {}
            
            status = result.get("status") if isinstance(result, dict) else None
            if status in ["completed", "complete"]:
                return result
            elif status == "failed":
                logger.error(f"Job failed: {result}")
                return None
            
            time.sleep(1)
        except Exception as e:
            logger.error(f"Error polling job status: {e}")
            time.sleep(1)
    
    return None


def generate_pinterest_description(listing: dict) -> str:
    """Generate Pinterest-optimized description (SEO keywords)"""
    title = listing.get("title", "")
    category = listing.get("category", "")
    niche = CATEGORY_TO_NICHE.get(category, "christian")
    
    niche_tags = {
        "military": "#USMC #MarineCorps #VeteranMade #MilitaryLife #SemperFi",
        "patriotic": "#USA #PatrioticDecor #AmericanMade #4thOfJuly #RedWhiteBlue",
        "christian": "#ChristianArt #FaithBased #ChurchDecor #ReformedTheology #Easter",
        "outdoor": "#HuntingLife #WildlifeArt #CabinDecor #OutdoorLife #NatureArt"
    }
    
    description = f"""✨ {title} ✨

Perfect for Cricut, Silhouette, laser cutters & vinyl cutting machines!

🎨 Instant digital download includes:
• SVG file (vector, infinitely scalable)
• PNG file (high resolution, transparent background)

💡 Create stunning:
• T-shirts & apparel
• Tumblers & mugs  
• Wall art & signs
• Car decals & stickers

{niche_tags.get(niche, niche_tags['christian'])} #SVGCutFile #DigitalDownload #CricutMade
"""
    return description.strip()


def generate_instagram_caption(listing: dict) -> str:
    """Generate Instagram caption (hashtags + engaging)"""
    title = listing.get("title", "")
    category = listing.get("category", "")
    niche = CATEGORY_TO_NICHE.get(category, "christian")
    
    # Short, punchy captions
    openers = {
        "military": "Semper Fi. 🦅",
        "patriotic": "Land of the free. 🇺🇸",
        "christian": "To God be the glory. ✝️",
        "outdoor": "Where the wild things are. 🦌"
    }
    
    hashtags = {
        "military": "#USMC #MarineCorps #Veteran #MilitaryLife #SemperFi #Marines #MilitaryWife #MilitaryFamily #ProudMarine #CricutMade #SVGFiles #DigitalDownload #VeteranOwned",
        "patriotic": "#USA #America #Patriotic #RedWhiteBlue #AmericanMade #4thOfJuly #ProudAmerican #CricutMade #SVGFiles #DIY #SmallBusiness",
        "christian": "#Christian #Faith #Jesus #Bible #ChurchDecor #Easter #Reformed #Scripture #CricutMade #SVGFiles #ChristianArt #FaithBased",
        "outdoor": "#Hunting #Wildlife #Outdoors #Nature #Deer #CabinLife #CountryLiving #CricutMade #SVGFiles #OutdoorLife #NatureArt"
    }
    
    opener = openers.get(niche, openers['christian'])
    tags = hashtags.get(niche, hashtags['christian'])
    
    caption = f"""{opener}

{title}

SVG cut file for Cricut, Silhouette & more.
Link in bio! 🔗

{tags}"""
    return caption.strip()


def generate_facebook_caption(listing: dict) -> str:
    """Generate Facebook caption (conversational, engagement-focused)"""
    title = listing.get("title", "")
    category = listing.get("category", "")
    niche = CATEGORY_TO_NICHE.get(category, "christian")
    
    # Conversational hooks
    hooks = {
        "military": "Know a Marine who'd love this? Tag them below! 🦅",
        "patriotic": "Perfect for your next patriotic project! What would you make with this? 🇺🇸",
        "christian": "This would look beautiful in a church or home. Who needs this design? ✝️",
        "outdoor": "Calling all outdoor lovers! What project would you use this for? 🦌"
    }
    
    hook = hooks.get(niche, hooks['christian'])
    
    caption = f"""{title}

{hook}

✨ Instant digital download
✨ SVG, PNG & more included
✨ Works with Cricut, Silhouette & laser cutters

Shop link in comments! 👇"""
    return caption.strip()


def schedule_pinterest_pin(
    media_info: dict,
    title: str,
    description: str,
    link: str,
    board_id: str,
    scheduled_at: str
) -> bool:
    """Schedule a Pinterest pin for future publishing."""
    headers = get_headers()
    
    payload = {
        "bulk": {
            "state": "scheduled",
            "posts": [
                {
                    "networks": {
                        "pinterest": {
                            "type": "photo",
                            "text": description[:500],
                            "title": title[:100],
                            "url": link,
                            "media": [
                                {
                                    "id": media_info["id"],
                                    "type": "photo",
                                    "path": media_info.get("path", ""),
                                    "thumbnail": media_info.get("thumbnail", "")
                                }
                            ]
                        }
                    },
                    "accounts": [
                        {
                            "id": PINTEREST_ACCOUNT_ID,
                            "album_id": board_id,
                            "scheduled_at": scheduled_at
                        }
                    ]
                }
            ]
        }
    }
    
    try:
        resp = requests.post(
            f"{PUBLER_API_BASE}/posts/schedule",
            headers=headers,
            json=payload
        )
        resp.raise_for_status()
        result = resp.json()
        
        job_id = result.get("job_id")
        if job_id:
            final = poll_job_status(job_id)
            if final:
                # Check if job completed without errors
                failures = final.get("payload", {}).get("failures", {})
                if not failures:  # Empty dict means no failures
                    return True
                else:
                    logger.error(f"Job completed with failures: {failures}")
                    return False
        return False
            
    except Exception as e:
        logger.error(f"Failed to schedule pin: {e}")
        return False


def schedule_instagram_post(
    media_info: dict,
    caption: str,
    scheduled_at: str
) -> bool:
    """Schedule an Instagram post."""
    headers = get_headers()
    
    payload = {
        "bulk": {
            "state": "scheduled",
            "posts": [
                {
                    "networks": {
                        "instagram": {
                            "type": "photo",
                            "text": caption[:2200],
                            "media": [
                                {
                                    "id": media_info["id"],
                                    "type": "photo",
                                    "path": media_info.get("path", ""),
                                    "thumbnail": media_info.get("thumbnail", "")
                                }
                            ]
                        }
                    },
                    "accounts": [
                        {
                            "id": INSTAGRAM_ACCOUNT_ID,
                            "scheduled_at": scheduled_at
                        }
                    ]
                }
            ]
        }
    }
    
    try:
        resp = requests.post(
            f"{PUBLER_API_BASE}/posts/schedule",
            headers=headers,
            json=payload
        )
        resp.raise_for_status()
        result = resp.json()
        
        job_id = result.get("job_id")
        if job_id:
            final = poll_job_status(job_id)
            if final:
                # Check if job completed without errors
                failures = final.get("payload", {}).get("failures", {})
                if not failures:  # Empty dict means no failures
                    return True
                else:
                    logger.error(f"Instagram job completed with failures: {failures}")
                    return False
        return False
            
    except Exception as e:
        logger.error(f"Failed to schedule Instagram post: {e}")
        return False


def schedule_facebook_post(
    media_info: dict,
    caption: str,
    link: str,
    scheduled_at: str
) -> bool:
    """Schedule a Facebook page post."""
    headers = get_headers()
    
    payload = {
        "bulk": {
            "state": "scheduled",
            "posts": [
                {
                    "networks": {
                        "facebook": {
                            "type": "photo",
                            "text": f"{caption}\n\n🛒 Shop: {link}",
                            "media": [
                                {
                                    "id": media_info["id"],
                                    "type": "photo",
                                    "path": media_info.get("path", ""),
                                    "thumbnail": media_info.get("thumbnail", "")
                                }
                            ]
                        }
                    },
                    "accounts": [
                        {
                            "id": FACEBOOK_ACCOUNT_ID,
                            "scheduled_at": scheduled_at
                        }
                    ]
                }
            ]
        }
    }
    
    try:
        resp = requests.post(
            f"{PUBLER_API_BASE}/posts/schedule",
            headers=headers,
            json=payload
        )
        resp.raise_for_status()
        result = resp.json()
        
        job_id = result.get("job_id")
        if job_id:
            final = poll_job_status(job_id)
            if final:
                # Check if job completed without errors
                failures = final.get("payload", {}).get("failures", {})
                if not failures:  # Empty dict means no failures
                    return True
                else:
                    logger.error(f"Facebook job completed with failures: {failures}")
                    return False
        return False
            
    except Exception as e:
        logger.error(f"Failed to schedule Facebook post: {e}")
        return False


def is_weekday(date: datetime) -> bool:
    """Check if date is a weekday (Mon=0 to Fri=4)"""
    return date.weekday() < 5


def schedule_listing_all_platforms(
    listing: dict,
    day_date: datetime,
    slot_index: int,
    data: dict
) -> Dict[str, int]:
    """Schedule a listing across all platforms. Returns counts by platform."""
    results = {"pinterest": 0, "instagram": 0, "facebook": 0}
    
    folder = find_archive_folder(listing)
    if not folder:
        logger.warning(f"No archive folder found for: {listing.get('title', 'Unknown')[:50]}")
        return results
    
    # Get first available image for the listing
    posted = set(listing.get("posted_images", []))
    scheduled = set(listing.get("scheduled_images", []))
    images = get_images_to_post(folder)
    remaining = [img for img in images if img.name not in posted and img.name not in scheduled]
    
    if not remaining:
        return results
    
    # Use the first image for Instagram/Facebook, all images for Pinterest
    first_image = remaining[0]
    
    # Upload image once
    media_info = upload_media(first_image)
    if not media_info:
        return results
    
    # Generate captions
    pinterest_desc = listing.get("pinterest_description") or generate_pinterest_description(listing)
    instagram_caption = generate_instagram_caption(listing)
    facebook_caption = generate_facebook_caption(listing)
    
    category = listing.get("category", "")
    board_id = PINTEREST_BOARD_IDS.get(category, PINTEREST_BOARD_IDS["Easter SVGs"])
    
    # Get Pinterest time slot
    pinterest_time = PINTEREST_TIME_SLOTS[slot_index]
    hour, minute = map(int, pinterest_time.split(":"))
    pinterest_scheduled = day_date.replace(hour=hour, minute=minute, second=0, microsecond=0)
    pinterest_at = pinterest_scheduled.strftime("%Y-%m-%dT%H:%M:%S") + "-08:00"
    
    # Schedule Pinterest (always)
    logger.info(f"Scheduling Pinterest: {listing.get('title', '')[:40]} at {pinterest_time}")
    if schedule_pinterest_pin(media_info, listing.get("title", ""), pinterest_desc, 
                              listing.get("url", ""), board_id, pinterest_at):
        results["pinterest"] = 1
        if "scheduled_images" not in listing:
            listing["scheduled_images"] = []
        listing["scheduled_images"].append(first_image.name)
        save_listings(data)
    
    # Schedule Instagram (using 2 PM slot listing, at 12 PM)
    if slot_index == 1:  # 2 PM Pinterest slot
        ig_hour, ig_minute = map(int, INSTAGRAM_TIME_SLOT.split(":"))
        ig_scheduled = day_date.replace(hour=ig_hour, minute=ig_minute, second=0, microsecond=0)
        ig_at = ig_scheduled.strftime("%Y-%m-%dT%H:%M:%S") + "-08:00"
        
        logger.info(f"Scheduling Instagram: {listing.get('title', '')[:40]} at {INSTAGRAM_TIME_SLOT}")
        if schedule_instagram_post(media_info, instagram_caption, ig_at):
            results["instagram"] = 1
    
    # Schedule Facebook (using 9 AM slot listing, at 10 AM) - weekdays only
    if slot_index == 0 and is_weekday(day_date):  # 9 AM Pinterest slot, weekday
        fb_hour, fb_minute = map(int, FACEBOOK_TIME_SLOT.split(":"))
        fb_scheduled = day_date.replace(hour=fb_hour, minute=fb_minute, second=0, microsecond=0)
        fb_at = fb_scheduled.strftime("%Y-%m-%dT%H:%M:%S") + "-08:00"
        
        logger.info(f"Scheduling Facebook: {listing.get('title', '')[:40]} at {FACEBOOK_TIME_SLOT}")
        if schedule_facebook_post(media_info, facebook_caption, listing.get("url", ""), fb_at):
            results["facebook"] = 1
    
    # For weekends, use any available slot for Instagram
    if not is_weekday(day_date) and slot_index == 0:  # Weekend, first slot
        ig_hour, ig_minute = map(int, INSTAGRAM_TIME_SLOT.split(":"))
        ig_scheduled = day_date.replace(hour=ig_hour, minute=ig_minute, second=0, microsecond=0)
        ig_at = ig_scheduled.strftime("%Y-%m-%dT%H:%M:%S") + "-08:00"
        
        logger.info(f"Scheduling Weekend Instagram: {listing.get('title', '')[:40]} at {INSTAGRAM_TIME_SLOT}")
        if schedule_instagram_post(media_info, instagram_caption, ig_at):
            results["instagram"] = 1
    
    time.sleep(1)  # Rate limit protection
    return results


def main():
    """Main entry point - schedule all platforms for the week"""
    logger.info("=" * 60)
    logger.info("Social Media Batch Scheduler - Starting Weekly Schedule")
    logger.info("Platforms: Pinterest, Instagram, Facebook")
    logger.info("=" * 60)
    
    data = load_listings()
    
    # Get listings to schedule
    max_listings = LISTINGS_PER_DAY * DAYS_TO_SCHEDULE
    listings = get_unposted_listings(data, max_listings)
    
    if not listings:
        logger.info("No unposted listings found")
        print("ALL_SCHEDULED")
        return
    
    logger.info(f"Found {len(listings)} listings to schedule")
    
    # Start scheduling from today
    start_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
    
    totals = {"pinterest": 0, "instagram": 0, "facebook": 0}
    listing_index = 0
    
    for day in range(DAYS_TO_SCHEDULE):
        if listing_index >= len(listings):
            break
            
        day_date = start_date + timedelta(days=day)
        weekday_name = day_date.strftime("%A")
        day_str = day_date.strftime("%Y-%m-%d")
        
        logger.info(f"\n--- {weekday_name}, {day_str} ---")
        
        # How many slots for this day
        slots_today = 3 if is_weekday(day_date) else 1
        
        for slot_idx in range(slots_today):
            if listing_index >= len(listings):
                break
                
            listing = listings[listing_index]
            results = schedule_listing_all_platforms(listing, day_date, slot_idx, data)
            
            for platform, count in results.items():
                totals[platform] += count
            
            listing_index += 1
    
    logger.info("=" * 60)
    logger.info(f"COMPLETE: Scheduled {listing_index} listings")
    logger.info(f"  Pinterest: {totals['pinterest']} posts")
    logger.info(f"  Instagram: {totals['instagram']} posts")
    logger.info(f"  Facebook:  {totals['facebook']} posts")
    logger.info("=" * 60)
    
    print(f"SUCCESS: Pinterest={totals['pinterest']}, Instagram={totals['instagram']}, Facebook={totals['facebook']}")


if __name__ == "__main__":
    main()
