#!/usr/bin/env bash
# docx Environment Setup & Initialization Script
# Supports: macOS (Homebrew), Linux (apt/dnf/pacman), WSL
# License: MIT
set -euo pipefail

# Force English output for dotnet CLI
export DOTNET_CLI_UI_LANGUAGE=en

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
DOTNET_DIR="$SCRIPT_DIR/dotnet"
LOG_FILE="$PROJECT_DIR/.setup.log"

# --- Colors ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log()   { echo -e "${GREEN}[OK]${NC}    $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
fail()  { echo -e "${RED}[FAIL]${NC}  $*"; }
info()  { echo -e "${BLUE}[INFO]${NC}  $*"; }
step()  { echo -e "\n${BLUE}=== $* ===${NC}"; }

# --- Detect OS & Package Manager ---
detect_platform() {
    OS="unknown"
    PKG_MGR="unknown"
    ARCH="$(uname -m)"

    case "$(uname -s)" in
        Darwin)
            OS="macos"
            if command -v brew &>/dev/null; then
                PKG_MGR="brew"
            else
                PKG_MGR="none"
            fi
            ;;
        Linux)
            OS="linux"
            if [ -f /etc/os-release ]; then
                . /etc/os-release
                case "$ID" in
                    ubuntu|debian|linuxmint|pop)
                        PKG_MGR="apt"
                        ;;
                    fedora|rhel|centos|rocky|alma)
                        PKG_MGR="dnf"
                        ;;
                    arch|manjaro|endeavouros)
                        PKG_MGR="pacman"
                        ;;
                    opensuse*|sles)
                        PKG_MGR="zypper"
                        ;;
                    alpine)
                        PKG_MGR="apk"
                        ;;
                    *)
                        PKG_MGR="unknown"
                        ;;
                esac
            fi
            # Detect WSL
            if grep -qi microsoft /proc/version 2>/dev/null; then
                OS="wsl"
            fi
            ;;
        MINGW*|MSYS*|CYGWIN*)
            OS="windows-git-bash"
            PKG_MGR="none"
            ;;
    esac

    echo "Platform: $OS ($ARCH), Package Manager: $PKG_MGR"
}

# --- .NET SDK Installation ---
install_dotnet() {
    step "Checking .NET SDK"

    if command -v dotnet &>/dev/null; then
        local ver
        ver=$(dotnet --version 2>/dev/null || echo "0")
        local major="${ver%%.*}"
        if [ "$major" -ge 8 ] 2>/dev/null; then
            log "dotnet $ver already installed (>= 8.0 OK)"
            return 0
        else
            warn "dotnet $ver found but < 8.0, upgrading..."
        fi
    fi

    info "Installing .NET SDK..."
    case "$PKG_MGR" in
        brew)
            brew install --cask dotnet-sdk
            ;;
        apt)
            # Microsoft package repo for Ubuntu/Debian
            if ! dpkg -l dotnet-sdk-8.0 &>/dev/null 2>&1; then
                info "Adding Microsoft package repository..."
                sudo apt-get update -qq
                sudo apt-get install -y -qq wget apt-transport-https
                wget -q "https://dot.net/v1/dotnet-install.sh" -O /tmp/dotnet-install.sh
                chmod +x /tmp/dotnet-install.sh
                /tmp/dotnet-install.sh --channel 8.0 --install-dir "$HOME/.dotnet"
                export PATH="$HOME/.dotnet:$PATH"
                echo 'export PATH="$HOME/.dotnet:$PATH"' >> "$HOME/.bashrc"
            fi
            ;;
        dnf)
            sudo dnf install -y dotnet-sdk-8.0
            ;;
        pacman)
            sudo pacman -S --noconfirm dotnet-sdk
            ;;
        zypper)
            sudo zypper install -y dotnet-sdk-8.0
            ;;
        apk)
            apk add --no-cache dotnet8-sdk
            ;;
        none)
            if [ "$OS" = "windows-git-bash" ]; then
                fail "On Windows, install .NET SDK from: https://dotnet.microsoft.com/download"
                fail "Then restart your terminal and re-run this script."
                return 1
            fi
            # Fallback: use Microsoft install script
            info "Using Microsoft install script..."
            wget -q "https://dot.net/v1/dotnet-install.sh" -O /tmp/dotnet-install.sh || \
                curl -sSL "https://dot.net/v1/dotnet-install.sh" -o /tmp/dotnet-install.sh
            chmod +x /tmp/dotnet-install.sh
            /tmp/dotnet-install.sh --channel 8.0 --install-dir "$HOME/.dotnet"
            export PATH="$HOME/.dotnet:$PATH"
            echo 'export PATH="$HOME/.dotnet:$PATH"' >> "$HOME/.bashrc"
            ;;
        *)
            warn "Unknown package manager. Install .NET SDK manually: https://dotnet.microsoft.com/download"
            return 1
            ;;
    esac

    # Verify
    if command -v dotnet &>/dev/null; then
        log "dotnet $(dotnet --version) installed"
    else
        fail "dotnet installation failed. Install manually: https://dotnet.microsoft.com/download"
        return 1
    fi
}

# --- Pandoc Installation (Optional) ---
install_pandoc() {
    step "Checking pandoc (optional: content preview)"

    if command -v pandoc &>/dev/null; then
        log "pandoc $(pandoc --version | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?') already installed"
        return 0
    fi

    info "Installing pandoc..."
    case "$PKG_MGR" in
        brew)   brew install pandoc ;;
        apt)    sudo apt-get install -y -qq pandoc ;;
        dnf)    sudo dnf install -y pandoc ;;
        pacman) sudo pacman -S --noconfirm pandoc ;;
        zypper) sudo zypper install -y pandoc ;;
        apk)    apk add --no-cache pandoc ;;
        *)
            warn "Cannot auto-install pandoc. Install manually: https://pandoc.org/installing.html"
            return 0
            ;;
    esac

    if command -v pandoc &>/dev/null; then
        log "pandoc installed"
    else
        warn "pandoc installation failed (optional, will degrade gracefully)"
    fi
}

# --- LibreOffice Installation (Optional) ---
install_soffice() {
    step "Checking LibreOffice/soffice (optional: .doc conversion)"

    if command -v soffice &>/dev/null; then
        log "soffice already installed"
        return 0
    fi

    # Also check common install paths
    local soffice_paths=(
        "/usr/bin/soffice"
        "/usr/local/bin/soffice"
        "/opt/libreoffice/program/soffice"
        "/snap/bin/libreoffice"
        "/Applications/LibreOffice.app/Contents/MacOS/soffice"
    )
    for p in "${soffice_paths[@]}"; do
        if [ -x "$p" ]; then
            log "soffice found at $p"
            if [ "$OS" = "macos" ] && [ "$p" = "/Applications/LibreOffice.app/Contents/MacOS/soffice" ]; then
                info "Tip: Add to PATH: ln -s '$p' /usr/local/bin/soffice"
            fi
            return 0
        fi
    done

    info "Installing LibreOffice (this may take a while)..."
    case "$PKG_MGR" in
        brew)   brew install --cask libreoffice ;;
        apt)    sudo apt-get install -y -qq libreoffice-core ;;
        dnf)    sudo dnf install -y libreoffice-core ;;
        pacman) sudo pacman -S --noconfirm libreoffice-still ;;
        zypper) sudo zypper install -y libreoffice ;;
        apk)    apk add --no-cache libreoffice ;;
        *)
            warn "Cannot auto-install LibreOffice. Install manually: https://www.libreoffice.org/download/"
            return 0
            ;;
    esac

    if command -v soffice &>/dev/null; then
        log "soffice installed"
    else
        warn "soffice not found after install (optional, .doc conversion unavailable)"
    fi
}

# --- zip/unzip ---
install_zip_tools() {
    step "Checking zip/unzip"

    local need_zip=false need_unzip=false
    command -v zip &>/dev/null   && log "zip already installed"   || need_zip=true
    command -v unzip &>/dev/null && log "unzip already installed" || need_unzip=true

    if ! $need_zip && ! $need_unzip; then
        return 0
    fi

    info "Installing zip/unzip..."
    case "$PKG_MGR" in
        brew)   brew install zip unzip 2>/dev/null || true ;;
        apt)    sudo apt-get install -y -qq zip unzip ;;
        dnf)    sudo dnf install -y zip unzip ;;
        pacman) sudo pacman -S --noconfirm zip unzip ;;
        zypper) sudo zypper install -y zip unzip ;;
        apk)    apk add --no-cache zip unzip ;;
        *)      warn "Install zip/unzip manually (optional, .NET handles DOCX natively)" ;;
    esac
}

# --- .NET Project Build ---
build_project() {
    step "Building docx .NET project"

    if [ ! -d "$DOTNET_DIR" ]; then
        fail "Dotnet project directory not found: $DOTNET_DIR"
        return 1
    fi

    cd "$DOTNET_DIR"

    info "Restoring NuGet packages..."
    if ! dotnet restore --verbosity quiet 2>>"$LOG_FILE"; then
        fail "NuGet restore failed. Check network and $LOG_FILE for details."
        fail "Common causes:"
        fail "  - No internet access (NuGet needs to download packages)"
        fail "  - Corporate proxy blocking nuget.org"
        fail "  - Disk space insufficient"
        echo ""
        fail "Try manually: cd $DOTNET_DIR && dotnet restore --verbosity detailed"
        return 1
    fi
    log "NuGet packages restored"

    info "Building project..."
    if ! dotnet build --verbosity quiet --no-restore 2>>"$LOG_FILE"; then
        fail "Build failed. Check $LOG_FILE for details."
        fail "Try manually: cd $DOTNET_DIR && dotnet build --verbosity normal"
        return 1
    fi
    log "Project built successfully"

    cd "$PROJECT_DIR"
}

# --- Shell Script Permissions ---
fix_permissions() {
    step "Setting script permissions"

    local scripts=(
        "$SCRIPT_DIR/env_check.sh"
        "$SCRIPT_DIR/docx_preview.sh"
        "$SCRIPT_DIR/doc_to_docx.sh"
        "$SCRIPT_DIR/setup.sh"
    )

    for s in "${scripts[@]}"; do
        if [ -f "$s" ]; then
            chmod +x "$s"
            log "chmod +x $(basename "$s")"
        fi
    done
}

# --- NuGet Proxy / Certificate Issues (Corporate Environments) ---
check_nuget_config() {
    step "Checking NuGet configuration"

    local nuget_config="$HOME/.nuget/NuGet/NuGet.Config"
    if [ -f "$nuget_config" ]; then
        log "NuGet config exists: $nuget_config"
    else
        info "No custom NuGet config found (using defaults)"
    fi

    # Test NuGet connectivity
    if dotnet nuget list source 2>/dev/null | grep -q "nuget.org"; then
        log "nuget.org source is configured"
    else
        warn "nuget.org not in sources. Adding..."
        dotnet nuget add source "https://api.nuget.org/v3/index.json" --name "nuget.org" 2>/dev/null || true
    fi
}

# --- Locale / Encoding Check ---
check_locale() {
    step "Checking locale and encoding"

    local current_lang="${LANG:-not set}"
    local current_lc="${LC_ALL:-not set}"

    if echo "$current_lang" | grep -qi "utf-8\|utf8"; then
        log "Locale supports UTF-8: LANG=$current_lang"
    else
        warn "Locale may not support UTF-8: LANG=$current_lang"
        warn "CJK document processing requires UTF-8. Set: export LANG=en_US.UTF-8"
        if [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then
            info "To fix permanently: sudo locale-gen en_US.UTF-8 && sudo update-locale LANG=en_US.UTF-8"
        fi
    fi
}

# --- Font Check (for CJK and professional documents) ---
check_fonts() {
    step "Checking fonts for document rendering"

    if [ "$OS" = "macos" ]; then
        # macOS has good CJK support built-in
        log "macOS: built-in CJK font support (PingFang, Hiragino, Apple SD Gothic)"
        log "macOS: built-in Western fonts (Helvetica, Times, Calibri via Office)"
        if [ -d "/Applications/Microsoft Word.app" ] || [ -d "/Applications/Microsoft Office" ]; then
            log "Microsoft Office fonts available (Calibri, Cambria, etc.)"
        else
            warn "Microsoft Office not installed — Calibri/Cambria fonts may be missing"
            info "Documents will render with fallback fonts on this machine"
            info "Recipients with Office installed will see correct fonts"
        fi
    elif [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then
        # Check for key font packages
        local missing_fonts=()

        if ! fc-list 2>/dev/null | grep -qi "liberation\|times new roman\|calibri"; then
            missing_fonts+=("Western: liberation-fonts or msttcorefonts")
        fi

        if ! fc-list 2>/dev/null | grep -qi "noto.*cjk\|wqy\|simsun\|pingfang"; then
            missing_fonts+=("CJK: noto-fonts-cjk or wqy-microhei")
        fi

        if [ ${#missing_fonts[@]} -eq 0 ]; then
            log "Font support looks good"
        else
            warn "Missing fonts may affect document rendering:"
            for f in "${missing_fonts[@]}"; do
                warn "  - $f"
            done
            info "Install fonts:"
            case "$PKG_MGR" in
                apt)
                    info "  sudo apt-get install -y fonts-liberation fonts-noto-cjk"
                    info "  # For MS core fonts: sudo apt-get install -y ttf-mscorefonts-installer"
                    ;;
                dnf)
                    info "  sudo dnf install -y liberation-fonts google-noto-sans-cjk-fonts"
                    ;;
                pacman)
                    info "  sudo pacman -S ttf-liberation noto-fonts-cjk"
                    ;;
                *)
                    info "  Install Liberation Fonts and Noto CJK fonts for your distribution"
                    ;;
            esac
        fi
    fi
}

# --- Verification Run ---
verify_installation() {
    step "Verification Test"

    local test_output="/tmp/docx-setup-test-$$.docx"

    info "Creating a test document..."
    if cd "$DOTNET_DIR" && dotnet run --project Docx.Cli -- create \
        --type report --output "$test_output" --title "Setup Test" 2>>"$LOG_FILE"; then
        log "Test document created: $test_output"

        # Try preview
        if command -v pandoc &>/dev/null; then
            local preview
            preview=$(pandoc -f docx -t plain "$test_output" 2>/dev/null | head -5)
            if [ -n "$preview" ]; then
                log "Preview working: \"$preview\""
            fi
        fi

        # Cleanup
        rm -f "$test_output"
        log "Test passed — docx is ready to use!"
    else
        fail "Test document creation failed. Check $LOG_FILE for details."
        return 1
    fi

    cd "$PROJECT_DIR"
}

# --- Summary ---
print_summary() {
    step "Setup Complete"

    echo ""
    echo "  Environment: $OS ($ARCH)"
    echo "  .NET SDK:    $(dotnet --version 2>/dev/null || echo 'NOT FOUND')"
    echo "  pandoc:      $(pandoc --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || echo 'not installed (optional)')"
    echo "  soffice:     $(soffice --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || echo 'not installed (optional)')"
    echo "  Project:     $DOTNET_DIR"
    echo ""
    echo "  Usage:"
    echo "    dotnet run --project $DOTNET_DIR/Docx.Cli -- create --type report --output my_report.docx"
    echo "    bash $SCRIPT_DIR/env_check.sh     # Quick environment check"
    echo ""
    echo "  Log file: $LOG_FILE"
}

# --- Main ---
main() {
    echo "============================================"
    echo "  docx Setup & Initialization"
    echo "  $(date '+%Y-%m-%d %H:%M:%S')"
    echo "============================================"

    : > "$LOG_FILE"  # Clear log

    detect_platform

    # Parse arguments
    local SKIP_OPTIONAL=false
    local SKIP_VERIFY=false
    for arg in "$@"; do
        case "$arg" in
            --minimal)      SKIP_OPTIONAL=true ;;
            --skip-verify)  SKIP_VERIFY=true ;;
            --help|-h)
                echo "Usage: setup.sh [options]"
                echo "  --minimal       Only install critical dependencies (skip pandoc, soffice, fonts)"
                echo "  --skip-verify   Skip the verification test at the end"
                echo "  --help          Show this help"
                exit 0
                ;;
        esac
    done

    install_dotnet
    install_zip_tools

    if ! $SKIP_OPTIONAL; then
        install_pandoc
        install_soffice
        check_fonts
    fi

    check_locale
    check_nuget_config
    fix_permissions
    build_project

    if ! $SKIP_VERIFY; then
        verify_installation
    fi

    print_summary
}

main "$@"
