import requests
import json
import time
import os
import collections
import getpass
import re

# --- Dependency Check and Setup ---
try:
    import google.generativeai as genai
except ImportError:
    print("Error: The 'google-generativeai' library is not installed.")
    print("Please install it by running: pip install google-generativeai")
    exit()

# --- Configuration ---
JIKAN_API_BASE_URL = "https://api.jikan.moe/v4"
CONTENT_FILE = "manga_content.json"
CONFIG_FILE = "config.ini"

# Jikan rate limiting
REQUEST_TIMESTAMPS = collections.deque()
REQUEST_LIMIT = 3  # 3 requests per second
TIME_WINDOW = 1    # 1 second

# --- API Key and Configuration Management ---
def get_api_key():
    """Gets the Google AI API key, prompting the user if not found."""
    # Check environment variable first
    api_key = os.getenv("GOOGLE_API_KEY")
    if api_key:
        print("Loaded Google API Key from environment variable.")
        input("Press Enter to continue...")
        return api_key

    # Check config file next
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'r') as f:
            config = json.load(f)
            api_key = config.get("GOOGLE_API_KEY")
            if api_key:
                print("Loaded Google API Key from config.ini.")
                input("Press Enter to continue...")
                return api_key

    # If not found, prompt the user
    print("\n--- Google AI API Key Required ---")
    print("To use the AI generation feature, you need a Google AI API Key.")
    print("You can get a free key from Google AI Studio.")
    print("The key will be stored locally in 'config.ini' so you don't have to enter it again.")
    
    api_key = getpass.getpass("Please enter your Google AI API Key: ")

    # Save the key to config.ini for future use
    with open(CONFIG_FILE, 'w') as f:
        json.dump({"GOOGLE_API_KEY": api_key}, f)
    
    print("API Key saved to config.ini.")
    return api_key


# --- Jikan API Interaction with Rate Limiting ---
def jikan_api_request(endpoint, params=None):
    """
    Makes a rate-limited request to the Jikan API.
    Waits if the request limit has been reached in the last second.
    """
    global REQUEST_TIMESTAMPS
    
    while True:
        now = time.time()
        # Remove timestamps older than the time window
        while REQUEST_TIMESTAMPS and REQUEST_TIMESTAMPS[0] < now - TIME_WINDOW:
            REQUEST_TIMESTAMPS.popleft()
        
        if len(REQUEST_TIMESTAMPS) < REQUEST_LIMIT:
            break
        
        # Calculate sleep time to respect the rate limit
        sleep_time = (REQUEST_TIMESTAMPS[0] + TIME_WINDOW) - now + 0.05 # small buffer
        print(f"Jikan rate limit reached. Waiting for {sleep_time:.2f} seconds...")
        time.sleep(sleep_time)

    try:
        REQUEST_TIMESTAMPS.append(time.time())
        print(f"Making Jikan request to: {JIKAN_API_BASE_URL}{endpoint}")
        response = requests.get(f"{JIKAN_API_BASE_URL}{endpoint}", params=params)
        response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"\n--- Jikan API Error --- \n{e}\n------------------")
        return None

# --- Helper Functions ---
def clear_screen():
    """Clears the console screen."""
    os.system('cls' if os.name == 'nt' else 'clear')

def get_choice(max_choice, allow_back=True):
    """Gets and validates a user's integer choice."""
    while True:
        try:
            prompt = "> "
            choice = input(prompt)
            if allow_back and choice.lower() == 'b':
                return 'b'
            choice = int(choice)
            if 1 <= choice <= max_choice:
                return choice
            else:
                print(f"Invalid choice. Please enter a number between 1 and {max_choice}.")
        except ValueError:
            print("Invalid input. Please enter a number.")

def format_manga_data(manga_obj):
    """Formats Jikan manga data into the structure needed for manga_content.json."""
    return {
        "id": manga_obj['mal_id'],
        "name": manga_obj.get('title_english') or manga_obj.get('title'),
        "image": manga_obj['images']['jpg']['large_image_url'],
        # Optional: Add description for the hero section if needed
        "description": manga_obj.get('synopsis', ''),
        "year": manga_obj.get('published', {}).get('prop', {}).get('from', {}).get('year')
    }

def search_and_select_manga():
    """Prompts user to search for a manga, displays results, and returns the selected one."""
    query = input("Enter search term (or 'b' to go back): ")
    if query.lower() == 'b':
        return None
        
    results = jikan_api_request("/manga", params={"q": query, "limit": 10})
    if not results or not results.get('data'):
        print("No results found.")
        input("Press Enter to continue...")
        return None

    clear_screen()
    print(f"--- Search Results for '{query}' ---")
    for i, manga in enumerate(results['data'], 1):
        year = manga.get('published', {}).get('prop', {}).get('from', {}).get('year', 'N/A')
        print(f"[{i}] {manga.get('title_english') or manga.get('title')} ({manga.get('type', 'N/A')}, {year})")
    
    print("\n[b] Back to previous menu")
    
    print("\nSelect a manga to add:")
    choice = get_choice(len(results['data']))
    if choice == 'b':
        return None
        
    return results['data'][choice - 1]

# --- Management Logic ---
def manage_spotlight(data):
    """Handles logic for managing the spotlight section."""
    while True:
        clear_screen()
        print("--- Manage Spotlight Section ---")
        if not data['spotlight']:
            print("Spotlight is currently empty.")
        else:
            for i, item in enumerate(data['spotlight'], 1):
                print(f"[{i}] {item['name']} (ID: {item['id']})")
        
        print("\nOptions:")
        print("[1] Add a manga to Spotlight")
        print("[2] Remove a manga from Spotlight")
        print("[b] Back to Main Menu")
        
        choice = input("> ").lower()
        
        if choice == '1':
            manga_obj = search_and_select_manga()
            if manga_obj:
                if any(item['id'] == manga_obj['mal_id'] for item in data['spotlight']):
                    print(f"'{manga_obj['title']}' is already in the spotlight.")
                else:
                    formatted = format_manga_data(manga_obj)
                    data['spotlight'].append(formatted)
                    print(f"Added '{formatted['name']}' to spotlight.")
                input("Press Enter to continue...")
        
        elif choice == '2':
            if not data['spotlight']:
                print("Nothing to remove.")
                input("Press Enter to continue...")
                continue
            print("Enter the number of the manga to remove (or 'b' to cancel):")
            remove_choice = get_choice(len(data['spotlight']))
            if remove_choice != 'b':
                removed = data['spotlight'].pop(remove_choice - 1)
                print(f"Removed '{removed['name']}' from spotlight.")
                input("Press Enter to continue...")

        elif choice == 'b':
            return

def manage_sections(data):
    """Handles logic for managing horizontal sections."""
    while True:
        clear_screen()
        print("--- Manage Horizontal Sections ---")
        if not data['sections']:
            print("No sections created yet.")
        else:
            for i, section in enumerate(data['sections'], 1):
                print(f"[{i}] {section['title']} ({len(section['items'])} items)")
        
        print("\nOptions:")
        print("[1] Create a new section")
        print("[2] Edit an existing section")
        print("[3] Delete a section")
        print("[b] Back to Main Menu")
        
        choice = input("> ").lower()

        if choice == '1':
            title = input("Enter title for new section: ")
            data['sections'].append({"title": title, "items": []})
            print(f"Section '{title}' created.")
            input("Press Enter...")
        
        elif choice == '2':
            if not data['sections']:
                print("No sections to edit.")
                input("Press Enter...")
                continue
            for i, section in enumerate(data['sections'], 1):
                print(f"[{i}] {section['title']} ({len(section['items'])} items)")
            print("Select a section to edit:")
            edit_choice = get_choice(len(data['sections']))
            if edit_choice != 'b':
                edit_section_menu(data['sections'][edit_choice - 1])

        elif choice == '3':
            if not data['sections']:
                print("No sections to delete.")
                input("Press Enter...")
                continue
            print("Select a section to delete:")
            delete_choice = get_choice(len(data['sections']))
            if delete_choice != 'b':
                removed = data['sections'].pop(delete_choice - 1)
                print(f"Deleted section '{removed['title']}'.")
                input("Press Enter...")

        elif choice == 'b':
            return

def edit_section_menu(section):
    """Menu for editing a specific section."""
    while True:
        clear_screen()
        print(f"--- Editing Section: {section['title']} ---")
        if not section['items']:
            print("This section is empty.")
        else:
            for i, item in enumerate(section['items'], 1):
                print(f"  [{i}] {item['name']} (ID: {item['id']})")
        
        print("\nOptions:")
        print("[1] Add a manga to this section (Manual Search)")
        print("[2] Remove a manga from this section")
        print("[3] Auto-populate this section (from Jikan)")
        print("[4] Generate content with AI")
        print("[5] Rename this section")
        print("[b] Back to Sections Menu")
        
        choice = input("> ").lower()

        if choice == '1':
            manga_obj = search_and_select_manga()
            if manga_obj:
                if any(item['id'] == manga_obj['mal_id'] for item in section['items']):
                    print(f"'{manga_obj['title']}' is already in this section.")
                else:
                    formatted = format_manga_data(manga_obj)
                    section['items'].append(formatted)
                    print(f"Added '{formatted['name']}' to '{section['title']}'.")
                input("Press Enter...")

        elif choice == '2':
            if not section['items']:
                print("Nothing to remove.")
                input("Press Enter...")
                continue
            print("Enter the number of the manga to remove:")
            remove_choice = get_choice(len(section['items']))
            if remove_choice != 'b':
                removed = section['items'].pop(remove_choice - 1)
                print(f"Removed '{removed['name']}' from '{section['title']}'.")
                input("Press Enter...")
        
        elif choice == '3':
            auto_populate_section(section)
        
        elif choice == '4':
            generate_with_ai(section)

        elif choice == '5':
            new_title = input(f"Enter new title for '{section['title']}': ")
            section['title'] = new_title
            print("Section renamed.")
            input("Press Enter...")

        elif choice == 'b':
            return

def auto_populate_section(section):
    """Automatically populates a section from a Jikan endpoint."""
    clear_screen()
    print(f"--- Auto-Populate Section: {section['title']} ---")
    print("Select a category to populate from:")
    print("[1] Top Manga by Popularity")
    print("[2] Top Manhwa")
    print("[3] Top Publishing Manga")
    print("[b] Cancel")

    choice = get_choice(3)
    if choice == 'b':
        return

    endpoint_map = {
        1: ("/top/manga", {"filter": "bypopularity", "limit": 15}),
        2: ("/manga", {"type": "manhwa", "order_by": "popularity", "limit": 15}),
        3: ("/top/manga", {"filter": "publishing", "limit": 15})
    }
    endpoint, params = endpoint_map[choice]

    results = jikan_api_request(endpoint, params=params)
    if not results or not results.get('data'):
        print("Could not fetch data for this category.")
        input("Press Enter...")
        return
    
    added_count = 0
    skipped_count = 0
    for manga_obj in results['data']:
        if not any(item['id'] == manga_obj['mal_id'] for item in section['items']):
            section['items'].append(format_manga_data(manga_obj))
            added_count += 1
        else:
            skipped_count += 1
    
    print(f"Added {added_count} new items and skipped {skipped_count} duplicates in '{section['title']}'.")
    input("Press Enter...")

def generate_with_ai(section):
    """Generates content for a section using Google's Generative AI."""
    clear_screen()
    print(f"--- AI Content Generation for: {section['title']} ---")
    
    # 1. Get and configure API Key
    try:
        api_key = get_api_key()
        genai.configure(api_key=api_key)
        model = genai.GenerativeModel('gemini-1.5-flash')
    except Exception as e:
        print(f"An error occurred while configuring the AI model: {e}")
        input("Press Enter to return.")
        return

    # 2. Get user prompt
    print("Describe the kind of manga/manhwa you want to find.")
    print("Examples: 'top 10 isekai manga', 'psychological thrillers similar to Monster', 'wholesome slice of life'")
    user_prompt = input("\nEnter your prompt: ")
    if not user_prompt:
        return

    # 3. Query the AI model with improved prompt
    print("\nAsking the AI for suggestions... this may take a moment.")
    full_prompt = f"""List exactly 10 manga that fit this description: '{user_prompt}'.

IMPORTANT: Follow this exact format for your response:
- Return ONLY the manga titles
- One title per line
- No numbers, bullets, dashes, or prefixes
- No descriptions or explanations
- No extra text before or after the list
- Use the most commonly known English or romanized title

Example format:
Berserk
Vagabond
One Piece

Your response for '{user_prompt}':"""
    
    try:
        response = model.generate_content(full_prompt)
        if not response.text:
            print("The AI returned an empty response. Please try again.")
            input("Press Enter to return.")
            return
            
        # Clean and parse the response more robustly
        ai_suggestions = []
        lines = response.text.strip().split('\n')
        
        for line in lines:
            # Clean each line of common prefixes and formatting
            cleaned = line.strip()
            # Remove common prefixes like "1.", "- ", "• ", etc.
            cleaned = re.sub(r'^[\d\.\-\•\*\s]+', '', cleaned)
            cleaned = cleaned.strip()
            
            if cleaned and len(cleaned) > 1:  # Ensure it's not just whitespace or single character
                ai_suggestions.append(cleaned)
        
        # Limit to 10 suggestions max
        ai_suggestions = ai_suggestions[:10]
        
    except Exception as e:
        print(f"An error occurred while communicating with the AI: {e}")
        input("Press Enter to return.")
        return

    if not ai_suggestions:
        print("The AI didn't return any valid suggestions. Try a different prompt.")
        input("Press Enter to return.")
        return

    print(f"\nAI suggested {len(ai_suggestions)} manga titles.")
    
    # 4. Process suggestions: Search Jikan and get user confirmation
    print("\n--- Confirm AI Suggestions ---")
    print("For each suggestion, I will find the closest match on MyAnimeList.")
    print("Please confirm if the match is correct.")
    
    confirmed_manga = []
    for i, suggestion in enumerate(ai_suggestions, 1):
        print(f"\n[{i}/{len(ai_suggestions)}] Searching for: '{suggestion}'...")
        results = jikan_api_request("/manga", params={"q": suggestion, "limit": 3})
        
        if not results or not results.get('data'):
            print(f"--> Could not find any match for '{suggestion}'.")
            continue
        
        # Show top match but also alternatives if the first doesn't seem right
        match = results['data'][0]
        title = match.get('title_english') or match.get('title')
        year = match.get('published', {}).get('prop', {}).get('from', {}).get('year', 'N/A')
        
        print(f"--> Best match: '{title}' ({match.get('type', 'N/A')}, {year})")
        
        # Show alternatives if available
        if len(results['data']) > 1:
            print("    Alternatives:")
            for j, alt in enumerate(results['data'][1:3], 2):
                alt_title = alt.get('title_english') or alt.get('title')
                alt_year = alt.get('published', {}).get('prop', {}).get('from', {}).get('year', 'N/A')
                print(f"    [{j}] {alt_title} ({alt.get('type', 'N/A')}, {alt_year})")
        
        while True:
            if len(results['data']) > 1:
                choice = input("    Choose: [1] Use best match, [2-3] Use alternative, [s] Skip, [Enter] Use best match: ").strip().lower()
            else:
                choice = input("    [Enter] Add this manga, [s] Skip: ").strip().lower()
            
            if choice == '' or choice == '1':
                selected_match = results['data'][0]
                break
            elif choice == 's':
                selected_match = None
                break
            elif choice in ['2', '3'] and len(results['data']) > int(choice) - 1:
                selected_match = results['data'][int(choice) - 1]
                break
            else:
                print("    Invalid choice. Please try again.")
        
        if selected_match:
            formatted = format_manga_data(selected_match)
            # Avoid adding duplicates
            if any(a['id'] == formatted['id'] for a in confirmed_manga):
                print(f"--> Already added '{formatted['name']}'. Skipping.")
            else:
                confirmed_manga.append(formatted)
                print(f"--> Added '{formatted['name']}' to the list.")
        else:
            print(f"--> Skipped '{suggestion}'.")

    # 5. Final review and add to section
    if not confirmed_manga:
        print("\nNo new manga were confirmed. Returning to menu.")
        input("Press Enter...")
        return

    clear_screen()
    print("--- Final Review ---")
    print("The following new manga will be added to the section:")
    for item in confirmed_manga:
        print(f"- {item['name']}")

    final_confirm = input("\nAdd these items to the section? [Y/n]: ").lower()
    if final_confirm == '' or final_confirm == 'y':
        added_count = 0
        skipped_count = 0
        for manga in confirmed_manga:
            if not any(item['id'] == manga['id'] for item in section['items']):
                section['items'].append(manga)
                added_count += 1
            else:
                skipped_count += 1
        print(f"\nSuccessfully added {added_count} new manga.")
        if skipped_count > 0:
            print(f"Skipped {skipped_count} manga that were already in the section.")
    else:
        print("Operation cancelled. No changes were made.")
    
    input("Press Enter to continue...")



# --- Main Application ---
def main():
    """Main function to run the content manager."""
    data = None
    
    # Check if a content file exists and prompt the user.
    if os.path.exists(CONTENT_FILE):
        clear_screen()
        print("--- Welcome Back ---")
        print(f"Found existing content file: '{CONTENT_FILE}'")
        print("\nWhat would you like to do?")
        print("[1] Load the existing content")
        print("[2] Start from scratch (Warning: saving will overwrite the old file)")

        while data is None:
            choice = input("> ")
            if choice == '1':
                try:
                    with open(CONTENT_FILE, 'r') as f:
                        data = json.load(f)
                        # Ensure the basic structure exists, in case the file is malformed
                        if 'spotlight' not in data: data['spotlight'] = []
                        if 'sections' not in data: data['sections'] = []
                    print("Content loaded successfully.")
                except (json.JSONDecodeError, FileNotFoundError):
                    print(f"Error: Could not read or parse '{CONTENT_FILE}'. Starting from scratch.")
                    data = {"spotlight": [], "sections": []}
            elif choice == '2':
                print("Starting with a blank slate.")
                data = {"spotlight": [], "sections": []}
            else:
                print("Invalid choice. Please enter 1 or 2.")
        input("Press Enter to continue...")
    else:
        # If no content file exists, start from scratch automatically.
        print(f"No '{CONTENT_FILE}' found. Starting with a blank slate.")
        data = {"spotlight": [], "sections": []}
        input("Press Enter to continue...")


    while True:
        clear_screen()
        print("--- Manga Content Manager ---")
        print(" (with AI-Powered Suggestions)")
        print("\nSelect an option:")
        print("[1] Manage Spotlight Section")
        print("[2] Manage Horizontal Sections")
        print("[3] Save and Exit")
        print("[4] Exit Without Saving")
        
        choice = input("> ")

        if choice == '1':
            manage_spotlight(data)
        elif choice == '2':
            manage_sections(data)
        elif choice == '3':
            with open(CONTENT_FILE, 'w') as f:
                json.dump(data, f, indent=4)
            print(f"Content saved to {CONTENT_FILE}.")
            break
        elif choice == '4':
            print("Exiting without saving changes.")
            break
        else:
            print("Invalid option. Please try again.")
            input("Press Enter to continue...")

if __name__ == "__main__":
    main()