import asyncio
import datetime
import sys
import re
import httpx
from notebooklm import NotebookLMClient, AudioFormat, AudioLength, ChatGoal, ChatResponseLength

# --- FULL EDITORIAL CONFIGURATION ---

CUSTOM_PERSONA = """
You are an expert technical analyst and world-class podcast producer specializing in deep-tech trends. 
Your goal is to provide insightful, comprehensive, and highly structured briefings for a professional audience. 
Always prioritize depth, technical accuracy, and strategic context. 
Avoid superficial summaries; instead, connect the dots between disparate technical developments.
You have a sharp eye for industry shifts and the socio-economic impact of new technologies.
"""

PROMPT_TECH_DAILY = """
Act as a Technical Editor-in-Chief and Podcast Producer. Using the provided articles, synthesize today's 'Tech Horizon' briefing. 

This text will serve as the primary source for a 'Deep Dive' podcast. Your synthesis must be dense, factual, and narrative.

**Editorial Guidelines:**
1. **The 'Big Picture':** Identify the 3 major news items redefining the industry today across all categories. Explain why they are crucial and how they interconnect.
2. **Thematic Synthesis:** Group other relevant news into logical blocks (e.g., 'AI Frontiers', 'Developer Tools & Productivity', 'Cybersecurity & Infrastructure'). Create fluid transitions between topics rather than just listing them.
3. **Trend Analysis:** Note points of convergence (e.g., how advancements in AI models are impacting DevOps workflows or UI/UX Design).
4. **Challenge or Controversy:** Raise at least one polemical question, ethical challenge, or major risk related to today's news to stimulate deep debate between the podcast hosts.
5. **Tone & Style:** Use a professional yet engaging tone. Avoid unnecessary jargon without sacrificing technical precision. Remove all promotional language or 'fluff'.

**Expected Structure:**
- **Catchy Title:** A strong, professional title for today's briefing.
- **Headline News:** Deep analysis of the 3 key items identified in the 'Big Picture'.
- **Thematic Synthesis:** Comprehensive sections grouping related technical news.
- **The Debate of the Day:** A dedicated section for the polemical question or ethical challenge.
- **Global Impact:** A final conclusion on the overall direction of the industry based on today's developments.

IMPORTANT: Generate the entire response in English. Be as detailed as possible to ensure a long, rich, and high-quality podcast generation.
"""

# --- UTILS ---

async def find_latest_newsletters():
    """Find the most recent available newsletters with proper browser simulation."""
    # categories = ["ai", "design", "dev", "devops", "infosec", "it", "founders", "tech"]
    categories = ["ai", "design", "dev", "founders"]
    today = datetime.date.today()
    
    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",
    }
    
    async with httpx.AsyncClient(headers=headers, follow_redirects=True, timeout=15.0) as client:
        for i in range(4): # Try today, yesterday, -2d, -3d
            target_date = today - datetime.timedelta(days=i)
            date_str = target_date.isoformat()
            available_urls = []
            
            print(f"Checking availability for {date_str}...")
            
            for cat in categories:
                url = f"https://tldr.tech/{cat}/{date_str}"
                try:
                    resp = await client.get(url)
                    if resp.status_code == 200 and date_str in str(resp.url) and date_str in resp.text:
                        available_urls.append(url)
                        print(f"  [OK] {cat}")
                except Exception:
                    pass
            
            if len(available_urls) >= 4:
                print(f"  ✅ Found {len(available_urls)} valid newsletters for {date_str}.")
                return date_str, available_urls
                
    return None, []

# --- MAIN WORKFLOW ---

async def generate_podcast():
    print("--- Searching for the latest valid newsletters ---")
    selected_date, newsletter_urls = await find_latest_newsletters()
    
    if not selected_date:
        print("❌ Error: No valid newsletters found.")
        return

    notebook_name = f"tldr-deepdive-{selected_date}"
    
    async with await NotebookLMClient.from_storage() as client:
        await client.settings.set_output_language("en")

        print(f"Creating notebook: {notebook_name}...")
        nb = await client.notebooks.create(notebook_name)
        nb_id = nb.id
        
        # 1. Add Newsletters
        print("Step 1: Analyzing newsletters...")
        nl_sources = []
        for url in newsletter_urls:
            try:
                s = await client.sources.add_url(nb_id, url)
                nl_sources.append(s)
            except Exception:
                continue

        nl_ids = [s.id for s in nl_sources]
        
        print("  Waiting for indexing...")
        while True:
            sources = await client.sources.list(nb_id)
            if all(s.status == 2 for s in sources if s.id in nl_ids):
                break
            await asyncio.sleep(5)

        # 2. Extract Article URLs
        print("Step 2: Extracting potential articles...")
        article_urls = set()
        exclude = ["tldr.tech", "advertise.tldr.tech", "twitter.com", "linkedin.com", "facebook.com", "reddit.com", "wsj.com", "nytimes.com", "bloomberg.com", "ft.com", "x.com", "github.com"]
        
        for nl_id in nl_ids:
            fulltext = await client.sources.get_fulltext(nb_id, nl_id)
            matches = re.findall(r'\[([\s\S]*?)\]\((https?://[^\)]+)\)', fulltext.content)
            
            if not matches:
                raw_urls = re.findall(r'https?://[^\s<>"]+|www\.[^\s<>"]+', fulltext.content)
                for url in raw_urls:
                    if not any(p in url.lower() for p in exclude):
                        article_urls.add(url.strip(').,'))
            else:
                for title, url in matches:
                    if not any(p in url.lower() for p in exclude) and not "(Sponsor)" in title and not "(Tool)" in title:
                        article_urls.add(url.strip())
        
        # 3. Cleanup Newsletters
        print(f"Step 3: Cleaning up ({len(nl_ids)} newsletters)...")
        for nl_id in nl_ids:
            try:
                await client.sources.delete(nb_id, nl_id)
            except Exception: pass
        
        # 4. Greedy Article Addition (Target: 49 successful adds)
        all_potential_urls = list(article_urls)
        print(f"  {len(all_potential_urls)} unique articles identified.")
        
        new_art_ids = []
        target_count = 49
        successful_count = 0
        
        print(f"Step 4: Adding articles (Greedy filling up to {target_count})...")
        for i, url in enumerate(all_potential_urls):
            if successful_count >= target_count:
                print(f"  ✅ Reached target of {target_count} articles.")
                break
                
            try:
                # Logging progress
                print(f"  [{successful_count+1}/{target_count}] Adding: {url[:50]}...")
                s = await client.sources.add_url(nb_id, url)
                new_art_ids.append(s.id)
                successful_count += 1
            except Exception:
                print(f"  ⚠️ Skipping inaccessible article: {url[:40]}...")
            
            # Throttle every 5 attempts to respect API
            if (i + 1) % 5 == 0:
                await asyncio.sleep(2)

        if not new_art_ids:
            print("❌ Error: No articles could be added.")
            return

        # 5. Wait for indexing
        print("  Waiting for indexing...")
        while True:
            sources = await client.sources.list(nb_id)
            ready_count = sum(1 for s in sources if s.id in new_art_ids and s.status == 2)
            # Proceed if at least 80% of successfully added articles are indexed
            if ready_count >= (len(new_art_ids) * 0.8):
                break
            print(f"  Indexing progress: {ready_count}/{len(new_art_ids)} articles ready...")
            await asyncio.sleep(5)

        # 6. Configure Chat
        print("Step 5: Configuring Chat Persona and Response Length...")
        await client.chat.configure(
            nb_id,
            goal=ChatGoal.CUSTOM,
            response_length=ChatResponseLength.LONGER,
            custom_prompt=CUSTOM_PERSONA
        )

        # 7. Generate Briefing
        print("Step 6: Generating detailed editorial briefing...")
        result = await client.chat.ask(nb_id, PROMPT_TECH_DAILY)
        
        briefing_source = await client.sources.add_text(
            nb_id, 
            title=f"Tech Horizon Briefing - {selected_date}", 
            content=result.answer
        )
        
        while True:
            src = await client.sources.get(nb_id, briefing_source.id)
            if src.status == 2: break
            await asyncio.sleep(2)

        # 8. Generate Audio
        print("Step 7: Generating audio podcast (Deep Dive, Long, EN)...")
        status = await client.artifacts.generate_audio(
            nb_id,
            source_ids=[briefing_source.id],
            audio_format=AudioFormat.DEEP_DIVE,
            audio_length=AudioLength.LONG,
            language="en"
        )
        
        final_status = await client.artifacts.wait_for_completion(nb_id, status.task_id, timeout=1800)
        
        if final_status.is_complete:
            print(f"\n✅ Podcast successfully generated for: {selected_date}!")
        else:
            print(f"\n❌ Failed: {final_status.status}")

def generate_podcast_entry():
    """Point d'entrée pour la commande generate-podcast."""
    try:
        asyncio.run(generate_podcast())
    except Exception as e:
        print(f"Fatal error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    generate_podcast_entry()
