#!/usr/bin/env python3
"""
MiniBook - A tool to create a minibook from a list of links.
Supports both MkDocs and Jinja2/HTML generation.
"""

import json
import os
import sys
from datetime import datetime
from pathlib import Path

import requests
import typer
import yaml
from jinja2 import Environment, FileSystemLoader


def validate_url(url, timeout=5):
    """
    Validate if a URL is accessible.

    Args:
        url (str): The URL to validate
        timeout (int, optional): Timeout in seconds for the request

    Returns:
        tuple: (is_valid, error_message) where is_valid is a boolean and error_message is a string
               error_message is None if the URL is valid
    """
    try:
        # Make a HEAD request to check if the URL is accessible
        # HEAD is more efficient than GET as it doesn't download the full content
        response = requests.head(url, timeout=timeout, allow_redirects=True)

        # If the HEAD request fails, try a GET request as some servers don't support HEAD
        if response.status_code >= 400:
            response = requests.get(url, timeout=timeout, allow_redirects=True)

        # Check if the response status code indicates success
        if response.status_code < 400:
            return True, None
        else:
            return False, f"HTTP error: {response.status_code}"

    except requests.exceptions.Timeout:
        return False, "Timeout error"
    except requests.exceptions.ConnectionError:
        return False, "Connection error"
    except requests.exceptions.RequestException as e:
        return False, f"Request error: {str(e)}"
    except Exception as e:
        return False, f"Unexpected error: {str(e)}"


def generate_html(title, links, description=None, timestamp=None, output_file="minibook.html"):
    """
    Generate an HTML page with the given title and links using Jinja2.

    Args:
        title (str): The title of the webpage
        links (list): A list of tuples with (name, url)
        description (str, optional): A description to include on the page
        timestamp (str, optional): A fixed timestamp for testing purposes
        output_file (str, optional): The output HTML file

    Returns:
        str: The path to the generated HTML file
    """
    # Set up Jinja2 environment
    template_dir = os.path.join(os.path.dirname(__file__), "templates")
    env = Environment(loader=FileSystemLoader(template_dir))
    template = env.get_template("html.j2")

    # Use the provided timestamp or generate a new one
    if timestamp is None:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # Render the template with our data
    html = template.render(title=title, links=links, description=description, timestamp=timestamp)

    # Save the HTML to a file
    with open(output_file, "w") as f:
        f.write(html)

    return output_file


def generate_mkdocs_project(title, links, description=None, timestamp=None, output_dir="minibook_site"):
    """
    Generate a MkDocs project with the given title and links.

    Args:
        title (str): The title of the webpage
        links (list): A list of tuples with (name, url)
        description (str, optional): A description to include on the page
        timestamp (str, optional): A fixed timestamp for testing purposes
        output_dir (str, optional): The directory to create the MkDocs project in

    Returns:
        str: The path to the generated MkDocs project
    """
    # Create the output directory if it doesn't exist
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # Create the docs directory
    docs_dir = os.path.join(output_dir, "docs")
    if not os.path.exists(docs_dir):
        os.makedirs(docs_dir)

    # Use the provided timestamp or generate a new one
    if timestamp is None:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # Create the index.md file
    index_content = f"# {title}\n\n"

    if description:
        index_content += f"*{description}*\n\n"

    index_content += "## Links\n\n"

    for name, url in links:
        index_content += f"* [{name}]({url})\n"

    index_content += f"\n\n*Generated by MiniBook on {timestamp}*\n"

    with open(os.path.join(docs_dir, "index.md"), "w") as f:
        f.write(index_content)

    # Create the mkdocs.yml file
    mkdocs_config = {"site_name": title, "theme": "material", "nav": [{"Home": "index.md"}]}

    with open(os.path.join(output_dir, "mkdocs.yml"), "w") as f:
        yaml.dump(mkdocs_config, f, default_flow_style=False)

    return output_dir


app = typer.Typer(help="Create a minibook from a list of links")


@app.command()
def entrypoint(
    title: str = typer.Option("My Links", "--title", "-t", help="Title of the minibook"),
    description: str | None = typer.Option(None, "--description", "-d", help="Description of the minibook"),
    output: str = typer.Option(None, "--output", "-o", help="Output directory"),
    links: str = typer.Option(
        None,
        "--links",
        "-l",
        help="JSON formatted links: can be a list of objects with name/url keys, a list of arrays, or a dictionary",
    ),
    timestamp: str | None = typer.Option(None, "--timestamp", help="Fixed timestamp for testing purposes"),
    format: str = typer.Option(
        "html", "--format", help="Output format: html or mkdocs", show_choices=True, case_sensitive=False
    ),
    validate_links: bool = typer.Option(False, "--validate-links", help="Validate that all links are accessible"),
) -> int:
    """Create a minibook from a list of links."""
    if format not in ["html", "mkdocs"]:
        typer.echo(f"Invalid format: {format}. Must be 'html' or 'mkdocs'.", err=True)
        return 1

    if links is None:
        typer.echo("No links provided. Exiting.", err=True)
        sys.exit(1)

    # Ensure links is a string, not a list
    # if isinstance(links, list):
    #     links = "\n".join(links)

    typer.echo(f"Parsing links: {links}")

    link_tuples = []

    # Try to parse as JSON first
    try:
        # Clean up the JSON string - remove leading/trailing whitespace and handle multi-line strings
        cleaned_links = links.strip()

        # Parse the JSON string into a Python object
        json_data = json.loads(cleaned_links)
        typer.echo(f"Parsed JSON data: {json_data}")
        typer.echo(f"Instance of JSON data: {type(json_data)}")

        # Handle different JSON formats
        if isinstance(json_data, list):
            # If it's a list of lists/arrays: [["name", "url"], ...]
            if all(isinstance(item, list) for item in json_data):
                for item in json_data:
                    if len(item) >= 2:
                        link_tuples.append((item[0], item[1]))
            # If it's a list of objects: [{"name": "...", "url": "..."}, ...]
            elif all(isinstance(item, dict) for item in json_data):
                for item in json_data:
                    if "name" in item and "url" in item:
                        link_tuples.append((item["name"], item["url"]))
        # If it's a dictionary: {"name1": "url1", "name2": "url2", ...}
        elif isinstance(json_data, dict):
            for name, url in json_data.items():
                link_tuples.append((name, url))

        typer.echo(f"Parsed JSON links: {link_tuples}")

    # Fall back to the original parsing logic for backward compatibility
    except (json.JSONDecodeError, TypeError):
        typer.echo("JSON parsing failed, falling back to legacy format")

    # Validate links if requested
    if validate_links:
        typer.echo("Validating links...")
        invalid_links = []

        with typer.progressbar(link_tuples) as progress:
            for name, url in progress:
                is_valid, error_message = validate_url(url)
                if not is_valid:
                    invalid_links.append((name, url, error_message))

        # Report invalid links
        if invalid_links:
            typer.echo(f"\nFound {len(invalid_links)} invalid links:", err=True)
            for name, url, error in invalid_links:
                typer.echo(f"  - {name} ({url}): {error}", err=True)

            # Ask user if they want to continue
            if not typer.confirm("Do you want to continue with invalid links?"):
                typer.echo("Aborting due to invalid links.", err=True)
                return 1
        else:
            typer.echo("All links are valid!")

    if format == "html":
        # Generate HTML using Jinja2
        output_file = Path(output) / "index.html"
        output_path = generate_html(title, link_tuples, description, timestamp, output_file)
        typer.echo(f"HTML minibook created successfully: {os.path.abspath(output_path)}")
    else:
        # Generate MkDocs project
        output_dir = output
        if output_dir.endswith(".html"):
            # If the output is specified as an HTML file but format is mkdocs,
            # use a default directory name
            output_dir = "minibook_site"

        output_path = generate_mkdocs_project(title, link_tuples, description, timestamp, output_dir)
        typer.echo(f"MkDocs minibook created successfully: {os.path.abspath(output_path)}")
        typer.echo(f"To build the site, run: cd {output_path} && mkdocs build")
        typer.echo(f"To serve the site locally, run: cd {output_path} && mkdocs serve")

    return 0


if __name__ == "__main__":
    app()  # pragma: no cover
