#!/usr/bin/env bash
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
# pdf-helper.sh - PDF operations helper using LibPDF
# Usage: pdf-helper.sh [command] [options]
#
# Commands:
#   info <file>              - Show PDF information (pages, form fields, etc.)
#   fields <file>            - List form field names and types
#   fill <file> <json>       - Fill form fields from JSON
#   merge <output> <files..> - Merge multiple PDFs
#   text <file>              - Extract text content
#   install                  - Install @libpdf/core
#   help                     - Show this help

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" || exit
source "${SCRIPT_DIR}/shared-constants.sh"

set -euo pipefail

# Check if bun or node is available
get_runtime() {
    if command -v bun &>/dev/null; then
        echo "bun"
    elif command -v node &>/dev/null; then
        echo "node"
    else
        echo ""
    fi
    return 0
}

# Check if @libpdf/core is installed
check_libpdf() {
    local runtime
    runtime=$(get_runtime)
    
    if [[ -z "$runtime" ]]; then
        echo -e "${RED}Error:${NC} Neither bun nor node found. Install one first." >&2
        return 1
    fi
    
    # Check in current project or global
    if [[ -f "package.json" ]] && grep -q "@libpdf/core" package.json 2>/dev/null; then
        return 0
    fi
    
    # Try to import (use ESM import for consistency)
    if [[ "$runtime" == "bun" ]] && bun -e "import('@libpdf/core')" &>/dev/null; then
        return 0
    elif [[ "$runtime" == "node" ]] && node --input-type=module -e "import('@libpdf/core')" &>/dev/null; then
        return 0
    fi
    
    echo -e "${YELLOW}Warning:${NC} @libpdf/core not found."
    echo -e "Install with: ${BLUE}npm install @libpdf/core${NC} or ${BLUE}bun add @libpdf/core${NC}"
    return 1
}

# Run TypeScript/JavaScript code
run_script() {
    local script="$1"
    local runtime
    runtime=$(get_runtime)
    
    if [[ "$runtime" == "bun" ]]; then
        bun -e "$script"
    else
        node --input-type=module -e "$script"
    fi
    return 0
}

# Show PDF info
cmd_info() {
    local file="$1"
    
    if [[ ! -f "$file" ]]; then
        echo -e "${RED}Error:${NC} File not found: $file" >&2
        return 1
    fi
    
    check_libpdf || return 1
    
    PDF_FILE="$file" run_script '
import { PDF } from "@libpdf/core";
import { readFileSync } from "fs";

const file = process.env.PDF_FILE;
const bytes = readFileSync(file);
const pdf = await PDF.load(bytes);
const pages = pdf.getPages();
const form = pdf.getForm();

console.log("File:", file);
console.log("Pages:", pages.length);

if (form) {
    const fields = form.getFields();
    console.log("Form fields:", fields.length);
} else {
    console.log("Form fields: 0 (no form)");
}

if (pages.length > 0) {
    const page = pages[0];
    console.log("Page size:", Math.round(page.width), "x", Math.round(page.height), "points");
}
'
}

# List form fields
cmd_fields() {
    local file="$1"
    
    if [[ ! -f "$file" ]]; then
        echo -e "${RED}Error:${NC} File not found: $file" >&2
        return 1
    fi
    
    check_libpdf || return 1
    
    PDF_FILE="$file" run_script '
import { PDF } from "@libpdf/core";
import { readFileSync } from "fs";

const file = process.env.PDF_FILE;
const bytes = readFileSync(file);
const pdf = await PDF.load(bytes);
const form = pdf.getForm();

if (!form) {
    console.log("No form found in this PDF.");
} else {
    const fields = form.getFields();
    if (fields.length === 0) {
        console.log("No form fields found.");
    } else {
        console.log("Form fields:");
        for (const field of fields) {
            const name = field.getName();
            const type = field.constructor.name.replace("PDF", "").replace("Field", "");
            console.log("  -", name, "(" + type + ")");
        }
    }
}
'
}

# Fill form fields
cmd_fill() {
    local file="$1"
    local json="$2"
    local output="${3:-${file%.pdf}-filled.pdf}"
    
    if [[ ! -f "$file" ]]; then
        echo -e "${RED}Error:${NC} File not found: $file" >&2
        return 1
    fi
    
    check_libpdf || return 1
    
    PDF_FILE="$file" PDF_JSON="$json" PDF_OUTPUT="$output" run_script '
import { PDF } from "@libpdf/core";
import { readFileSync, writeFileSync } from "fs";

const file = process.env.PDF_FILE;
const jsonData = process.env.PDF_JSON;
const outputFile = process.env.PDF_OUTPUT;

const bytes = readFileSync(file);
const pdf = await PDF.load(bytes);
const form = pdf.getForm();

if (!form) {
    console.error("Error: No form found in this PDF.");
    process.exit(1);
}

const data = JSON.parse(jsonData);
const result = form.fill(data);
console.log("Filled fields:", result.filled.join(", ") || "none");
if (result.skipped.length > 0) {
    console.log("Skipped fields:", result.skipped.join(", "));
}

const output = await pdf.save();
writeFileSync(outputFile, output);
console.log("Filled PDF saved to:", outputFile);
'
}

# Merge PDFs
cmd_merge() {
    if ! command -v jq &>/dev/null; then
        echo -e "${RED}Error:${NC} 'jq' is not installed. Please install it to use the merge command." >&2
        return 1
    fi
    
    local output="$1"
    shift
    local files=("$@")
    
    if [[ ${#files[@]} -lt 2 ]]; then
        echo -e "${RED}Error:${NC} Need at least 2 files to merge" >&2
        return 1
    fi
    
    check_libpdf || return 1
    
    local files_json
    files_json=$(printf '%s\n' "${files[@]}" | jq -R . | jq -s .)
    
    PDF_FILES="$files_json" PDF_OUTPUT="$output" run_script '
import { PDF } from "@libpdf/core";
import { readFileSync, writeFileSync } from "fs";

const files = JSON.parse(process.env.PDF_FILES);
const outputFile = process.env.PDF_OUTPUT;
const pdfs = files.map(f => readFileSync(f));

const merged = await PDF.merge(pdfs);
const output = await merged.save();
writeFileSync(outputFile, output);
console.log("Merged", files.length, "PDFs into:", outputFile);
'
}

# Extract text
cmd_text() {
    local file="$1"
    
    if [[ ! -f "$file" ]]; then
        echo -e "${RED}Error:${NC} File not found: $file" >&2
        return 1
    fi
    
    check_libpdf || return 1
    
    PDF_FILE="$file" run_script '
import { PDF } from "@libpdf/core";
import { readFileSync } from "fs";

const file = process.env.PDF_FILE;
const bytes = readFileSync(file);
const pdf = await PDF.load(bytes);
const pages = pdf.getPages();

for (let i = 0; i < pages.length; i++) {
    const result = pages[i].extractText();
    if (pages.length > 1) {
        console.log("--- Page", i + 1, "---");
    }
    console.log(result.text);
}
'
}

# Install @libpdf/core
cmd_install() {
    local runtime
    runtime=$(get_runtime)
    
    if [[ -z "$runtime" ]]; then
        echo -e "${RED}Error:${NC} Neither bun nor node found. Install one first." >&2
        return 1
    fi
    
    echo -e "${BLUE}Installing @libpdf/core...${NC}"
    
    if [[ "$runtime" == "bun" ]]; then
        bun add @libpdf/core
    else
        npm install @libpdf/core
    fi
    
    echo -e "${GREEN}Done!${NC}"
    return 0
}

# Show help
cmd_help() {
    cat << 'EOF'
pdf-helper.sh - PDF operations helper using LibPDF

Usage: pdf-helper.sh [command] [options]

Commands:
  info <file>              - Show PDF information (pages, form fields, etc.)
  fields <file>            - List form field names and types
  fill <file> <json> [out] - Fill form fields from JSON
  merge <output> <files..> - Merge multiple PDFs
  text <file>              - Extract text content
  install                  - Install @libpdf/core
  help                     - Show this help

Examples:
  # Show PDF info
  pdf-helper.sh info document.pdf

  # List form fields
  pdf-helper.sh fields form.pdf

  # Fill form fields
  pdf-helper.sh fill form.pdf '{"name":"John","email":"john@example.com"}'

  # Merge PDFs
  pdf-helper.sh merge combined.pdf doc1.pdf doc2.pdf doc3.pdf

  # Extract text
  pdf-helper.sh text document.pdf

Requirements:
  - Node.js 20+ or Bun
  - @libpdf/core (install with: npm install @libpdf/core)

For more advanced operations (signing, encryption, etc.), use LibPDF directly
in your TypeScript/JavaScript code. See: https://libpdf.dev
EOF
}

# Main
main() {
    local cmd="${1:-help}"
    local file_arg
    shift || true
    
    case "$cmd" in
        info)
            [[ $# -lt 1 ]] && { echo -e "${RED}Error:${NC} Missing file argument" >&2; return 1; }
            file_arg="$1"
            cmd_info "$file_arg"
            ;;
        fields)
            [[ $# -lt 1 ]] && { echo -e "${RED}Error:${NC} Missing file argument" >&2; return 1; }
            file_arg="$1"
            cmd_fields "$file_arg"
            ;;
        fill)
            [[ $# -lt 2 ]] && { echo -e "${RED}Error:${NC} Missing file or json argument" >&2; return 1; }
            cmd_fill "$@"
            ;;
        merge)
            [[ $# -lt 3 ]] && { echo -e "${RED}Error:${NC} Need output file and at least 2 input files" >&2; return 1; }
            cmd_merge "$@"
            ;;
        text)
            [[ $# -lt 1 ]] && { echo -e "${RED}Error:${NC} Missing file argument" >&2; return 1; }
            file_arg="$1"
            cmd_text "$file_arg"
            ;;
        install)
            cmd_install
            ;;
        help|--help|-h)
            cmd_help
            ;;
        *)
            echo -e "${RED}Error:${NC} Unknown command: $cmd" >&2
            echo "Run 'pdf-helper.sh help' for usage"
            return 1
            ;;
    esac
}

main "$@"
