#!/usr/bin/env bash
set -eo pipefail

# This script takes a markdown post, crafts the corresponding URL, checks if it's accessible,
# takes a screenshot, and saves it to a specified location.
# It can update the front matter of the post with the path to the generated image (-u | --update-front-matter option).
# It's meant to be used as a pre-commit hook to generate social media cards for Zola sites using the tabi theme.
# More details: https://osc.garden/blog/automating-social-media-cards-zola/

function help_function(){
    echo "This script automates the creation of social media cards for Zola websites."
    echo "It takes a Markdown post and saves its live screenshot to a specified location."
    echo ""
    echo "IMPORTANT! It needs to be run from the root of the Zola site."
    echo ""
    echo "Usage: social-cards-zola [OPTIONS]"
    echo ""
    echo "Options:"
    echo "  -h, --help                  Show this help message and exit."
    echo "  -b, --base_url URL          The base URL where the Zola site is hosted. Default is http://127.0.0.1:1111."
    echo "  -i, --input INPUT_PATH      The relative path to the markdown file of the post/section you want to capture. Should be in the format 'content/blog/post_name.language.md'."
    echo "  -k, --key KEY               The front matter key to update. Default is 'social_media_card'."
    echo "  -o, --output_path PATH      The directory where the generated image will be saved."
    echo "  -p, --print_output          Print the path to the resulting screenshot at the end."
    echo "  -u, --update-front-matter   Update or add the 'social_media_card' key in the front matter of the Markdown file."
    echo
    echo "Examples:"
    echo "  social-cards-zola --base_url https://example.com --input content/blog/my_post.md --output_path static/img/social_cards"
    echo "  social-cards-zola -u -b http://127.0.0.1:1025 -i content/archive/_index.es.md -o static/img"
    exit 0
}

function convert_filename_to_url() {
    # Remove .md extension.
    local post_name="${1%.md}"

    # Remove "content/" prefix.
    local url="${post_name#content/}"

    # Extract language code.
    local lang_code="${url##*.}"
    if [[ "$lang_code" == "$url" ]]; then
        lang_code=""  # No language code.
    else
        lang_code="${lang_code}/"  # Add trailing slash.
        url="${url%.*}"  # Remove the language code from the URL.
    fi

    # Handle co-located index.md by stripping it and using the directory as the URL.
    if [[ "$url" == */index ]]; then
        url="${url%/*}"  # Remove the /index suffix.
    fi

    # Remove "_index" suffix.
    if [[ "$url" == *"_index"* ]]; then
        url="${url%%_index*}"
    fi

    url=$(echo "$url" | sed -r 's/([0-9]{4}-[0-9]{2}-[0-9]{2}-)//')  # Replace datetime.

    # Return the final URL with a single trailing slash.
    full_url="${lang_code}${url}"
    echo "${full_url%/}/"
}

function error_exit() {
    echo "ERROR: $1" >&2
    exit "${2:-1}"
}

function validate_input_params() {
    missing_params=()
    if [[ -z "$base_url" ]]; then
        missing_params+=("base_url")
    fi
    if [[ -z "$input" ]]; then
        missing_params+=("input")
    fi
    if [[ -z "$output_path" ]]; then
        missing_params+=("output_path")
    fi

    if [ ${#missing_params[@]} -ne 0 ]; then
        error_exit "The following required settings are missing: ${missing_params[*]}. Use -h or --help for usage."
    fi
}

function check_dependencies() {
    for cmd in "curl" "shot-scraper"; do
        if ! command -v $cmd &> /dev/null; then
            error_exit "$cmd could not be found. Please install it."
        fi
    done
}

function fetch_status() {
    local retry_count=0
    local max_retries=5
    local status
    while [[ $retry_count -lt $max_retries ]]; do
        status=$(curl -s -o /dev/null -I -w "%{http_code}" "${base_url}${post_url}")
        if [[ "$status" -eq "200" ]]; then
            return
        fi
        retry_count=$((retry_count + 1))
        sleep 2
    done
    error_exit "Post $input is not accessible. Max retries ($max_retries) reached."
}

function capture_screenshot() {
    temp_file=$(mktemp /tmp/social-zola.XXXXXX)
    trap 'rm -f "$temp_file"' EXIT
    shot-scraper --silent "${base_url}/${post_url}" -w 700 -h 400 --retina --quality 60 -o "$temp_file"
}

function move_file() {
    local safe_filename=$(echo "${post_url%/}" | sed 's/[^a-zA-Z0-9]/_/g')

    # Create the output directory if it doesn't exist.
    mkdir -p "$output_path"

    image_filename="${output_path}/${safe_filename:-index}.jpg"  # If the filename is empty, use "index".
    mv "$temp_file" "$image_filename" || error_exit "Failed to move the file to $image_filename"
}

function update_front_matter {
    local md_file_path="$1"
    local image_output="${2#static/}"
    # Temporary file for awk processing
    temp_awk=$(mktemp /tmp/frontmatter.XXXXXX)

    awk -v card_path="$image_output" '
    # Initialize flags for tracking state.
    BEGIN { in_extra=done=front_matter=extra_exists=0; }

    # Function to insert the social_media_card path.
    function insert_card() { print "social_media_card = \"" card_path "\""; done=1; }

    {
        # If card has been inserted, simply output remaining lines.
        if (done) { print; next; }

        # Toggle front_matter flag at its start, denoted by +++
        if (/^\+\+\+/ && front_matter == 0) {
            front_matter = 1;
            print "+++";
            next;
        }

        # Detect [extra] section and set extra_exists flag.
        if (/^\[extra\]/) { in_extra=1; extra_exists=1; print; next; }

        # Update existing social_media_card.
        if (in_extra && /^social_media_card =/) { insert_card(); in_extra=0; next; }

        # End of front matter or start of new section.
        if (in_extra && (/^\[[a-zA-Z_-]+\]/ || (/^\+\+\+/ && front_matter == 1))) {
            insert_card();  # Add the missing social_media_card.
            in_extra=0;
        }

        # Insert missing [extra] section.
        if (/^\+\+\+/ && front_matter == 1 && in_extra == 0 && extra_exists == 0) {
            print "\n[extra]";
            insert_card();
            in_extra=0;
            front_matter = 0;
            print "+++";
            next;
        }

        # Print all other lines as-is.
        print;
    }' "$md_file_path" > "$temp_awk"

    # Move the temporary file back to the original markdown file.
    mv "$temp_awk" "$md_file_path"
}

function main() {
    while [[ "$#" -gt 0 ]]; do
        case "$1" in
            -h|--help)
                help_function;;
            -b|--base_url)
                base_url="$2"
                shift 2;;
            -i|--input)
                input="$2"
                shift 2;;
            -o|--output_path)
                output_path="$2"
                shift 2;;
            -k|--key)
                front_matter_key="$2"
                shift 2;;
            -u|--update-front-matter)
                update="true"
                shift 1;;
            -p|--print_output)
                print_output="true"
                shift 1;;
            *)
                error_exit "Unknown option: $1";;
        esac
    done

    validate_input_params
    check_dependencies

    : "${base_url:="http://127.0.0.1:1111"}"
    : "${front_matter_key:="social_media_card"}"
    base_url="${base_url%/}/"  # Ensure one trailing slash.
    post_url="$(convert_filename_to_url "$input")"

    fetch_status
    capture_screenshot
    move_file

    if [[ "$update" == "true" ]]; then
        update_front_matter "$input" "$image_filename"
    fi

    if [[ "$print_output" == "true" ]]; then
        echo "$image_filename"
    fi
}

main "$@"