# main.py

import os
import json
import logging
import asyncio
from google import genai
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from google.oauth2.credentials import Credentials
from dotenv import load_dotenv
from logging.handlers import RotatingFileHandler
from bs4 import BeautifulSoup
import requests
from telegram_scraper import TelegramScraper

# -------------------------------
# Configuration and Setup
# -------------------------------

# Load environment variables from .env file
from pathlib import Path
env_path = Path(__file__).parent.parent / ".env"
load_dotenv(dotenv_path=env_path, override=True)
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
YOUTUBE_API_CLIENT_SECRET_FILE = os.getenv("YOUTUBE_API_CLIENT_SECRET_FILE", "credentials.json")
YOUTUBE_API_YOUTUBE_TOKEN_FILE = os.getenv("YOUTUBE_API_YOUTUBE_TOKEN_FILE", "youtube_token.json")
YOUTUBE_API_BLOGGER_TOKEN_FILE = os.getenv("YOUTUBE_API_BLOGGER_TOKEN_FILE", "blogger_token.json")
BLOG_ID = os.getenv("BLOG_ID")  # Your Blogger Blog ID
YOUTUBE_MAX_RESULTS = int(os.getenv("YOUTUBE_MAX_RESULTS", 10))
VIEW_COUNT_THRESHOLD = int(os.getenv("VIEW_COUNT_THRESHOLD", 10000))
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
TELEGRAM_CHANNEL_ID = os.getenv("TELEGRAM_CHANNEL_ID")
TELEGRAM_API_ID = int(os.getenv("TELEGRAM_API_ID", "20898819"))
TELEGRAM_API_HASH = os.getenv("TELEGRAM_API_HASH", "")
TELEGRAM_PHONE = os.getenv("TELEGRAM_PHONE", "")
TELEGRAM_CHANNELS = os.getenv("TELEGRAM_CHANNELS", "@apkpure,@APKUpdates,@AndroidAPKShare").split(",")
TELEGRAM_CHANNELS = [ch.strip() for ch in TELEGRAM_CHANNELS]  # Clean up whitespace
YOUTUBE_SCOPES = [
    'https://www.googleapis.com/auth/youtube.force-ssl'
]
BLOGGER_SCOPES = [
    'https://www.googleapis.com/auth/blogger'
]

# -------------------------------
# Set up logging
# -------------------------------

def setup_logging():
    """
    Configure logging for the bot with separate handlers for file and console.
    """
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)  # Set root logger to the lowest level

    # Create handlers
    file_handler = RotatingFileHandler('bot.log', maxBytes=5*1024*1024, backupCount=5)
    file_handler.setLevel(logging.DEBUG)  # File handler logs all levels

    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)  # Console handler logs INFO and above

    # Create formatters and add them to handlers
    formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
    file_handler.setFormatter(formatter)
    console_handler.setFormatter(formatter)

    # Add handlers to the logger
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

# -------------------------------
# Helper Functions
# -------------------------------

def authenticate_youtube(credentials_path, token_path):
    """
    Authenticate and return the YouTube API client.
    """
    creds = None
    # Check if token file exists
    if os.path.exists(token_path):
        try:
            creds = Credentials.from_authorized_user_file(token_path, YOUTUBE_SCOPES)
        except Exception as e:
            logging.error(f"Error loading YouTube token file: {e}")
            creds = None
    # If no valid credentials, let the user log in
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            try:
                creds.refresh(Request())
                logging.info("YouTube credentials refreshed.")
            except Exception as e:
                logging.error(f"Error refreshing YouTube credentials: {e}")
                creds = None
        if not creds or not creds.valid:
            try:
                flow = InstalledAppFlow.from_client_secrets_file(credentials_path, YOUTUBE_SCOPES)
                creds = flow.run_local_server(port=0)
                logging.info("YouTube OAuth flow completed.")
            except Exception as e:
                logging.error(f"Error during YouTube OAuth flow: {e}")
                raise
        # Save the credentials for the next run
        with open(token_path, 'w') as token:
            token.write(creds.to_json())
            logging.info(f"YouTube credentials saved to {token_path}.")
    try:
        youtube = build('youtube', 'v3', credentials=creds)
        return youtube
    except Exception as e:
        logging.error(f"Failed to build YouTube API client: {e}")
        raise

def authenticate_blogger(credentials_path, token_path):
    """
    Authenticate and return the Blogger API client using OAuth 2.0.
    """
    creds = None
    # Check if token file exists
    if os.path.exists(token_path):
        try:
            creds = Credentials.from_authorized_user_file(token_path, BLOGGER_SCOPES)
        except Exception as e:
            logging.error(f"Error loading Blogger token file: {e}")
            creds = None
    # If no valid credentials, let the user log in
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            try:
                creds.refresh(Request())
                logging.info("Blogger credentials refreshed.")
            except Exception as e:
                logging.error(f"Error refreshing Blogger credentials: {e}")
                creds = None
        if not creds or not creds.valid:
            try:
                flow = InstalledAppFlow.from_client_secrets_file(credentials_path, BLOGGER_SCOPES)
                creds = flow.run_local_server(port=0)
                logging.info("Blogger OAuth flow completed.")
            except Exception as e:
                logging.error(f"Error during Blogger OAuth flow: {e}")
                raise
        # Save the credentials for the next run
        with open(token_path, 'w') as token:
            token.write(creds.to_json())
            logging.info(f"Blogger credentials saved to {token_path}.")
    try:
        blogger = build('blogger', 'v3', credentials=creds)
        return blogger
    except Exception as e:
        logging.error(f"Failed to build Blogger API client: {e}")
        raise

def search_videos(youtube, query, max_results=10):
    """
    Search for videos based on the query and return a list of video items.
    """
    try:
        request = youtube.search().list(
            part="snippet",
            q=query,
            maxResults=max_results,
            order="viewCount",  # Orders by view count
            type="video"
        )
        response = request.execute()
        items = response.get('items', [])
        if not items:
            logging.warning(f"No videos found for query: {query}")
            return []
        return items
    except HttpError as e:
        logging.error(f"HTTP error during YouTube search: {e}")
        raise
    except Exception as e:
        logging.error(f"Unexpected error during YouTube search: {e}")
        raise

def get_video_statistics(youtube, video_id):
    """
    Fetch the statistics of a video, including view count.
    """
    try:
        request = youtube.videos().list(
            part="statistics",
            id=video_id
        )
        response = request.execute()
        items = response.get('items', [])
        if not items:
            logging.warning(f"No statistics found for video ID: {video_id}")
            return None
        return items[0]['statistics']
    except HttpError as e:
        logging.error(f"HTTP error during fetching video statistics: {e}")
        raise
    except Exception as e:
        logging.error(f"Unexpected error during fetching video statistics: {e}")
        raise

def get_video_transcript(youtube, video_id, language='en'):
    """
    Fetch the transcript of a video in the specified language.
    Returns the transcript text or None if not available.
    """
    try:
        # List available caption tracks for the video
        captions_response = youtube.captions().list(
            part="snippet",
            videoId=video_id
        ).execute()
        
        captions = captions_response.get('items', [])
        if not captions:
            logging.info(f"No captions found for video ID: {video_id}")
            return None
        
        # Find captions in the desired language
        desired_captions = [cap for cap in captions if cap['snippet']['language'] == language]
        if not desired_captions:
            logging.info(f"No captions found in '{language}' for video ID: {video_id}")
            return None
        
        # Assuming the first matching caption is desired
        caption_id = desired_captions[0]['id']
        
        # Download the caption track
        caption_response = youtube.captions().download(
            id=caption_id
        ).execute()
        
        # Parse the TTML/XML content using BeautifulSoup
        soup = BeautifulSoup(caption_response, 'html.parser')
        texts = soup.find_all('text')
        transcript = ' '.join([text.get_text() for text in texts])
        transcript = transcript.strip()
        
        return transcript
    except HttpError as e:
        logging.error(f"HTTP error during fetching captions for video ID {video_id}: {e}")
        return None
    except Exception as e:
        logging.error(f"Unexpected error during fetching captions for video ID {video_id}: {e}")
        return None

def create_blog_post(blogger, blog_id, title, content, labels=None):
    """
    Create a new blog post in Blogger.
    """
    try:
        body = {
            "title": title,
            "content": content
        }
        if labels:
            body["labels"] = labels
        post = blogger.posts().insert(blogId=blog_id, body=body).execute()
        return post
    except HttpError as e:
        logging.error(f"HTTP error during Blogger post creation: {e}")
        raise
    except Exception as e:
        logging.error(f"Unexpected error during Blogger post creation: {e}")
        raise

def load_posted_videos(file_path):
    """
    Load the list of already posted video IDs to prevent duplicates.
    """
    if os.path.exists(file_path):
        try:
            with open(file_path, 'r') as f:
                return json.load(f)
        except json.JSONDecodeError as e:
            logging.error(f"Error decoding JSON from {file_path}: {e}")
            return []
    return []

def save_posted_videos(file_path, videos):
    """
    Save the updated list of posted video IDs.
    """
    try:
        with open(file_path, 'w') as f:
            json.dump(videos, f)
        logging.info(f"Posted videos list updated: {file_path}")
    except Exception as e:
        logging.error(f"Error saving posted videos to {file_path}: {e}")

def send_telegram_message(post_title, post_url, video_title):
    """
    Send a message to Telegram channel with the blog post link.
    """
    if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHANNEL_ID:
        logging.warning("Telegram bot token or channel ID not configured. Skipping Telegram notification.")
        return
    
    try:
        # Create message with formatting
        message = f"""
✅ <b>New Blog Post Published!</b>

📝 <b>Title:</b> {post_title}

🎥 <b>Video:</b> {video_title}

🔗 <b>Read Full Post:</b>
{post_url}
        """.strip()
        
        # Send message to Telegram
        url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
        payload = {
            "chat_id": TELEGRAM_CHANNEL_ID,
            "text": message,
            "parse_mode": "HTML"
        }
        
        response = requests.post(url, json=payload, timeout=10)
        
        if response.status_code == 200:
            logging.info(f"Telegram message sent successfully to channel {TELEGRAM_CHANNEL_ID}")
        else:
            logging.error(f"Failed to send Telegram message: {response.text}")
            
    except Exception as e:
        logging.error(f"Error sending Telegram message: {e}")

def generate_post_content(title, description, transcript=None, video_id=None):
    """
    Generate the blog post content using Google's Gemini API in HTML format.
    Creates a natural, engaging post based on actual video content.
    """
    # Prepare video context
    video_context = f"<strong>Title:</strong> {title}\n<strong>Description:</strong> {description}"
    if transcript:
        video_context += f"\n<strong>Transcript (summary):</strong> {transcript[:2000]}..."  # Limit transcript length
    
    prompt = f"""You are a professional content writer and blogger with expertise in creating engaging, natural blog posts.

Your task is to write a comprehensive, well-structured blog post in clean HTML format based on the following YouTube video information.

VIDEO INFORMATION:
{video_context}

REQUIREMENTS:
1. Make the content feel natural and authentic - NOT templated or generic
2. Extract and expand on the main topic/theme from the video
3. Use the video title as the main topic, NOT as a section heading
4. Structure the post with 3-5 relevant sections based on the content
5. Use proper HTML tags: <h2> for section headings, <p> for paragraphs, <ul>/<li> for lists
6. Ensure each section is meaningful and directly related to the video content
7. Include an engaging introduction that hooks the reader
8. Add a conclusion with relevant insights or takeaways
9. Do NOT include generic sections like "Key Features & Benefits" or "Limited-Time Offer"
10. Do NOT use placeholder text or example data
11. Generate only the inner HTML content (no <html>, <body>, or <h1> tags)
12. Make it readable, well-spaced, and visually organized

Generate the blog post now:"""
    
    try:
        # Create client with API key
        client = genai.Client(api_key=GEMINI_API_KEY)
        
        # Generate content using new API (gemini-2.5-flash is free tier)
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=prompt,
        )
        generated_html = response.text.strip()
        
        if not generated_html:
            logging.error("Gemini API returned empty content.")
            raise Exception("Gemini API returned empty content.")
        
        # Clean up markdown code fences if present
        if generated_html.startswith("```html"):
            generated_html = generated_html[7:]  # Remove ```html
        if generated_html.startswith("```"):
            generated_html = generated_html[3:]  # Remove ```
        if generated_html.endswith("```"):
            generated_html = generated_html[:-3]  # Remove trailing ```
        
        return generated_html.strip()
    except Exception as e:
        logging.error(f"Error generating content with Gemini API: {e}")
        raise

# -------------------------------
# Main Function
# -------------------------------

async def main():
    # Setup logging
    setup_logging()
    logging.info("Bot started.")
    print("Bot started.")  # Immediate feedback

    # Check for necessary configurations
    if not GEMINI_API_KEY:
        logging.error("Gemini API key not found. Please set GEMINI_API_KEY in the .env file.")
        print("Gemini API key not found. Please set GEMINI_API_KEY in the .env file.")
        return

    if not BLOG_ID:
        logging.error("Blogger Blog ID not found. Please set BLOG_ID in the .env file.")
        print("Blogger Blog ID not found. Please set BLOG_ID in the .env file.")
        return

    # Authenticate to YouTube and Blogger APIs
    try:
        youtube = authenticate_youtube(YOUTUBE_API_CLIENT_SECRET_FILE, YOUTUBE_API_YOUTUBE_TOKEN_FILE)
        logging.info("YouTube API client initialized.")
        print("YouTube API client initialized.")
    except Exception as e:
        logging.error(f"Failed to initialize YouTube API client: {e}")
        print(f"Failed to initialize YouTube API client: {e}")
        return

    try:
        blogger = authenticate_blogger(YOUTUBE_API_CLIENT_SECRET_FILE, YOUTUBE_API_BLOGGER_TOKEN_FILE)
        logging.info("Blogger API client initialized.")
        print("Blogger API client initialized.")
    except Exception as e:
        logging.error(f"Failed to initialize Blogger API client: {e}")
        print(f"Failed to initialize Blogger API client: {e}")
        return

    # Define search queries and labels
    queries = ["How to bypass activation key on app", "Hack aviator predictor application", "remove activation code"]  # Customize your search queries
    labels = ["Activation code", "Apk Editor Pro","Aviator Predictor"]  # Customize labels as needed

    # Load list of already posted videos
    posted_videos_file = "posted_videos.json"
    posted_videos = load_posted_videos(posted_videos_file)

    # Iterate over each query
    for query in queries:
        logging.info(f"Processing query: {query}")
        print(f"Processing query: {query}")  # Immediate feedback

        try:
            videos = search_videos(youtube, query, max_results=YOUTUBE_MAX_RESULTS)
            if not videos:
                logging.info(f"No videos found for query: {query}")
                print(f"No videos found for query: {query}")
                continue
            posted = False  # Flag to check if a video was posted in this category

            for video in videos:
                video_id = video['id']['videoId']
                if video_id in posted_videos:
                    logging.info(f"Video {video_id} has already been posted. Skipping.")
                    print(f"Video {video_id} has already been posted. Skipping.")
                    continue

                stats = get_video_statistics(youtube, video_id)
                if stats and int(stats.get('viewCount', 0)) >= VIEW_COUNT_THRESHOLD:
                    title = video['snippet']['title']
                    description = video['snippet']['description']
                    logging.info(f"Found suitable Video: {title} (ID: {video_id}) with {stats.get('viewCount')} views.")
                    print(f"Found suitable Video: {title} (ID: {video_id}) with {stats.get('viewCount')} views.")

                    # Attempt to fetch transcript
                    transcript = get_video_transcript(youtube, video_id, language='en')
                    if transcript:
                        logging.info(f"Transcript fetched for video ID: {video_id}.")
                        print(f"Transcript fetched for video ID: {video_id}.")
                    else:
                        logging.info(f"No transcript available for video ID: {video_id}. Proceeding without transcript.")
                        print(f"No transcript available for video ID: {video_id}. Proceeding without transcript.")

                    try:
                        ai_content = generate_post_content(title, description, transcript, video_id)
                        logging.debug(f"AI-generated content: {ai_content}")  # Retained for detailed logs
                        print("AI-generated content successfully.")
                    except Exception as e:
                        logging.error(f"Failed to generate content for video {video_id}: {e}")
                        print(f"Failed to generate content for video {video_id}: {e}")
                        continue

                    # Get YouTube video thumbnail URL
                    thumbnail_url = f"https://img.youtube.com/vi/{video_id}/maxresdefault.jpg"

                    # Create embedded YouTube video iframe with responsive styling
                    embedded_video = f"""
                    <div style="text-align: center; margin: 30px 0;">
                        <iframe width="100%" height="400" style="max-width: 700px; display: block; margin: 0 auto;" src="https://www.youtube.com/embed/{video_id}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
                    </div>
                    """

                    # Construct the complete HTML content with modern styling
                    complete_content = f"""
                    <style>
                        .post-header {{ margin-bottom: 30px; }}
                        .post-thumbnail {{ text-align: center; margin: 20px 0; }}
                        .post-thumbnail img {{ max-width: 100%; height: auto; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }}
                        .post-content {{ line-height: 1.8; font-size: 16px; color: #333; }}
                        .post-content h2 {{ margin-top: 25px; margin-bottom: 12px; color: #1a73e8; border-bottom: 2px solid #e8f0fe; padding-bottom: 8px; font-size: 22px; }}
                        .post-content p {{ margin-bottom: 15px; }}
                        .post-content ul, .post-content ol {{ margin-left: 20px; margin-bottom: 15px; }}
                        .post-content li {{ margin-bottom: 8px; }}
                        .post-content strong {{ color: #1a73e8; }}
                        .video-section {{ background-color: #f9f9f9; padding: 20px; border-radius: 8px; margin: 30px 0; }}
                    </style>
                    
                    <h1 style="font-size: 28px; margin-bottom: 15px; color: #202124;">{title}</h1>
                    
                    <div class="post-thumbnail">
                        <img src="{thumbnail_url}" alt="{title}">
                    </div>
                    
                    <div class="post-content">
                        {ai_content}
                    </div>
                    
                    <div class="video-section">
                        <h2>Watch the Video</h2>
                        {embedded_video}
                    </div>
                    """

                    # Create blog post
                    try:
                        response = create_blog_post(blogger, BLOG_ID, title, complete_content, labels)
                        post_url = response.get('url')
                        if post_url:
                            logging.info(f"Blog post created: {post_url}")
                            print(f"Blog post created: {post_url}")
                            
                            # Send notification to Telegram
                            send_telegram_message(title, post_url, video['snippet']['title'])
                        else:
                            logging.warning(f"Blog post created but no URL returned for video {video_id}.")
                            print(f"Blog post created but no URL returned for video {video_id}.")
                    except Exception as e:
                        logging.error(f"Failed to create blog post for video {video_id}: {e}")
                        print(f"Failed to create blog post for video {video_id}: {e}")
                        continue

                    # Update posted videos list
                    posted_videos.append(video_id)
                    save_posted_videos(posted_videos_file, posted_videos)
                    posted = True  # Video posted, move to next category
                    break  # Post only one video per query

            if not posted:
                logging.info(f"No eligible videos found for query: {query}")
                print(f"No eligible videos found for query: {query}")
        except Exception as e:
            logging.error(f"An error occurred while processing query '{query}': {e}")
            print(f"An error occurred while processing query '{query}': {e}")
            continue

    # ====== TELEGRAM CHANNELS SCRAPING SECTION ======
    logging.info("Starting Telegram channels scraping...")
    print("\n📱 Starting Telegram channels scraping...")
    
    try:
        telegram_scraper = TelegramScraper()
        posted_telegram_file = "posted_telegram_apps.json"
        posted_telegram = load_posted_videos(posted_telegram_file)
        
        if await telegram_scraper.connect():
            try:
                telegram_apps = await telegram_scraper.scrape_channels(TELEGRAM_CHANNELS)
                
                if telegram_apps:
                    for channel_key, app in telegram_apps.items():
                        app_hash = hash(app['url'])
                        
                        if app_hash in posted_telegram:
                            logging.info(f"App already posted: {app['title']}")
                            print(f"⏭️  Already posted: {app['title']}")
                            continue
                        
                        logging.info(f"Creating blog post for app: {app['title']}")
                        print(f"📝 Creating post: {app['title']} from {channel_key}")
                        
                        try:
                            response = create_blog_post_from_software(
                                blogger, 
                                BLOG_ID, 
                                app, 
                                labels=["App", "Telegram", channel_key]
                            )
                            
                            if response:
                                post_url = response.get('url')
                                if post_url:
                                    logging.info(f"App blog post created: {post_url}")
                                    print(f"✅ App post created: {post_url}")
                                    
                                    send_telegram_message(
                                        f"New App: {app['title']}", 
                                        post_url, 
                                        f"From Telegram - {channel_key}"
                                    )
                                    
                                    posted_telegram.append(app_hash)
                                    save_posted_videos(posted_telegram_file, posted_telegram)
                                    break  # Post one app per channel per run
                                else:
                                    logging.warning(f"App post created but no URL returned")
                            else:
                                logging.error(f"Failed to create post for app")
                                
                        except Exception as e:
                            logging.error(f"Error creating post from Telegram: {e}")
                            print(f"❌ Error: {e}")
                            continue
                else:
                    logging.info("No apps found in Telegram channels")
                    print("No apps found in Telegram channels")
                    
            finally:
                await telegram_scraper.disconnect()
        else:
            logging.warning("Failed to connect to Telegram")
            print("Failed to connect to Telegram")
            
    except Exception as e:
        logging.error(f"Error during Telegram scraping: {e}")
        print(f"Error during Telegram scraping: {e}")

    logging.info("Bot execution completed.")
    print("Bot execution completed.")

# -------------------------------
# Entry Point
# -------------------------------

if __name__ == "__main__":
    asyncio.run(main())
