import argparse
import json
import logging
import uuid
import time
import requests

# New v3 JSON Endpoints
BASE_URL = "https://www.microsoft.com/software-download-connector/api"
PROFILE = "606624d44113"
LOCALE = "en-US"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"

def setup_session():
    """Initializes a session with Microsoft tracking servers."""
    session_id = str(uuid.uuid4())
    return session_id

def get_product(product_id, session_id):
    """Fetches product/SKU info for a given ID."""
    url = f"{BASE_URL}/getskuinformationbyproductedition"
    params = {
        "profile": PROFILE,
        "productEditionId": product_id,
        "SKU": "undefined",
        "friendlyFileName": "undefined",
        "Locale": LOCALE,
        "sessionID": session_id
    }
    headers = {
        "User-Agent": UA, 
        "Accept": "application/json",
        "Referer": "https://www.microsoft.com/en-us/software-download/windows11"
    }
    
    try:
        r = requests.get(url, params=params, headers=headers, timeout=10)
        if not r.ok: return None
        
        # Handle Microsoft's double-encoded JSON
        raw_text = r.text
        try:
            # First pass: decode the wrapper JSON
            data = json.loads(raw_text)
            # Second pass: if the value is a string, it's the real JSON
            if isinstance(data, str):
                data = json.loads(data)
            return data
        except:
            return None
    except Exception as e:
        logging.error(f"Error fetching ID {product_id}: {e}")
        return None

def scan_id(product_id):
    """Checks if a product ID is valid and active."""
    session_id = setup_session()
    data = get_product(product_id, session_id)
    
    if not data or "Skus" not in data or len(data["Skus"]) == 0:
        return None
    
    if "Errors" in data and data["Errors"] and len(data["Errors"]) > 0:
        return None

    # MS usually puts the release name in the first SKU
    skus = data.get("Skus", [])
    if skus:
        # Check multiple possible name keys
        s = skus[0]
        # ReleaseName is often "Windows 10 Version 22H2 (Updated Oct 2025)"
        name = s.get("EditionName") or s.get("ReleaseName") or s.get("FriendlyName")
        if name:
            return name
            
    return f"Windows Product {product_id}"

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Scan for new Windows Product IDs (v3 JSON API)")
    parser.add_argument("--first", required=True, type=int, help="First ID to check")
    parser.add_argument("--last", required=True, type=int, help="Last ID to check")
    parser.add_argument("--write", help="Output JSON file")
    args = parser.parse_args()

    logging.basicConfig(level=logging.INFO, format='%(message)s')
    products = {}

    logging.info(f"Scanning range {args.first} to {args.last}...")

    for i in range(args.first, args.last + 1):
        name = scan_id(i)
        if name:
            logging.info(f"✅ FOUND: [{i}] {name}")
            products[str(i)] = name
        else:
            # Only log skips for very small ranges to keep output clean
            if (args.last - args.first) < 20:
                logging.info(f"❌ SKIP:  [{i}] Not found")
            products[str(i)] = False
        
        # Be gentle to the API
        time.sleep(0.5)

    if args.write:
        import os
        catalog = {}
        if os.path.exists(args.write):
            try:
                with open(args.write, 'r', encoding='utf-8') as f:
                    catalog = json.load(f)
            except Exception as e:
                logging.error(f"Failed to read existing catalog: {e}")
        
        # Merge new products, preserving existing extra metadata if present
        for pid, name in products.items():
            if pid in catalog:
                if name is False:
                    if isinstance(catalog[pid], dict):
                        catalog[pid]["active"] = False
                    else:
                        catalog[pid] = {
                            "name": catalog[pid],
                            "badge": "",
                            "archs": [],
                            "related": [],
                            "active": False
                        }
                    continue
                
                # Update name but keep other fields like badge, archs, etc.
                if isinstance(catalog[pid], dict):
                    catalog[pid]["name"] = name
                    catalog[pid]["active"] = True
                else:
                    # Upgrade from old flat format
                    catalog[pid] = {
                        "name": name,
                        "badge": "",
                        "archs": [],
                        "related": [],
                        "active": True
                    }
            else:
                if name is False:
                    continue # Do not add new dead entries to the catalog
                catalog[pid] = {
                    "name": name,
                    "badge": "",
                    "archs": [],
                    "related": [],
                    "active": True
                }
                
        with open(args.write, 'w', encoding='utf-8') as f:
            json.dump(catalog, f, indent=4)
            logging.info(f"\nSaved {len(catalog)} products to {args.write} (merged with existing)")
    
    logging.info("Done.")
