#!/bin/bash
# triple-c-scheduler — CLI for managing scheduled tasks in Triple-C containers
# Tasks are stored as JSON files and crontab is rebuilt from them as the source of truth.

set -euo pipefail

SCHEDULER_DIR="${HOME}/.claude/scheduler"
TASKS_DIR="${SCHEDULER_DIR}/tasks"
LOGS_DIR="${SCHEDULER_DIR}/logs"
NOTIFICATIONS_DIR="${SCHEDULER_DIR}/notifications"

# ── Helpers ──────────────────────────────────────────────────────────────────

ensure_dirs() {
    mkdir -p "$TASKS_DIR" "$LOGS_DIR" "$NOTIFICATIONS_DIR"
}

generate_id() {
    head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n'
}

rebuild_crontab() {
    local tmp
    tmp=$(mktemp)
    # Header
    echo "# Triple-C scheduled tasks — managed by triple-c-scheduler" > "$tmp"
    echo "# Do not edit manually; changes will be overwritten." >> "$tmp"
    echo "" >> "$tmp"

    for task_file in "$TASKS_DIR"/*.json; do
        [ -f "$task_file" ] || continue
        local enabled schedule id
        enabled=$(jq -r '.enabled' "$task_file")
        [ "$enabled" = "true" ] || continue
        schedule=$(jq -r '.schedule' "$task_file")
        id=$(jq -r '.id' "$task_file")
        echo "$schedule /usr/local/bin/triple-c-task-runner $id" >> "$tmp"
    done

    crontab "$tmp" 2>/dev/null || true
    rm -f "$tmp"
}

usage() {
    cat <<'EOF'
Usage: triple-c-scheduler <command> [options]

Commands:
  add          Add a new scheduled task
  remove       Remove a task
  enable       Enable a disabled task
  disable      Disable a task
  list         List all tasks
  logs         Show execution logs
  run          Manually trigger a task now
  notifications  Show or clear completion notifications

Add options:
  --name NAME          Task name (required)
  --prompt "TASK"      Task prompt for Claude (required)
  --schedule "CRON"    Cron schedule expression (for recurring tasks)
  --at "DATETIME"      Target datetime as "YYYY-MM-DD HH:MM" (for one-time tasks)
  --working-dir DIR    Working directory (default: /workspace)

Remove/Enable/Disable/Run options:
  --id ID              Task ID (required)

Logs options:
  --id ID              Show logs for a specific task (optional)
  --tail N             Show last N lines (default: 50)

Notifications options:
  --clear              Clear all notifications

Examples:
  triple-c-scheduler add --name "run-tests" --schedule "*/30 * * * *" --prompt "Run the test suite and report results"
  triple-c-scheduler add --name "friday-commit" --at "2026-03-06 16:00" --prompt "Commit all changes with a descriptive message"
  triple-c-scheduler list
  triple-c-scheduler logs --id a1b2c3d4 --tail 20
  triple-c-scheduler run --id a1b2c3d4
EOF
}

# ── Commands ─────────────────────────────────────────────────────────────────

cmd_add() {
    local name="" prompt="" schedule="" at="" working_dir="/workspace"

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --name)     name="$2"; shift 2 ;;
            --prompt)   prompt="$2"; shift 2 ;;
            --schedule) schedule="$2"; shift 2 ;;
            --at)       at="$2"; shift 2 ;;
            --working-dir) working_dir="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ -z "$name" ]; then
        echo "Error: --name is required" >&2
        return 1
    fi
    if [ -z "$prompt" ]; then
        echo "Error: --prompt is required" >&2
        return 1
    fi
    if [ -z "$schedule" ] && [ -z "$at" ]; then
        echo "Error: either --schedule or --at is required" >&2
        return 1
    fi
    if [ -n "$schedule" ] && [ -n "$at" ]; then
        echo "Error: use either --schedule or --at, not both" >&2
        return 1
    fi

    local id task_type cron_expr
    id=$(generate_id)

    if [ -n "$at" ]; then
        task_type="once"
        # Parse "YYYY-MM-DD HH:MM" into cron expression
        local year month day hour minute
        if ! [[ "$at" =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})\ ([0-9]{2}):([0-9]{2})$ ]]; then
            echo "Error: --at must be in format 'YYYY-MM-DD HH:MM'" >&2
            return 1
        fi
        year="${BASH_REMATCH[1]}"
        month="${BASH_REMATCH[2]}"
        day="${BASH_REMATCH[3]}"
        hour="${BASH_REMATCH[4]}"
        minute="${BASH_REMATCH[5]}"
        # Remove leading zeros for cron
        month=$((10#$month))
        day=$((10#$day))
        hour=$((10#$hour))
        minute=$((10#$minute))
        cron_expr="$minute $hour $day $month *"
    else
        task_type="recurring"
        cron_expr="$schedule"
    fi

    local created_at
    created_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

    local task_json
    task_json=$(jq -n \
        --arg id "$id" \
        --arg name "$name" \
        --arg prompt "$prompt" \
        --arg schedule "$cron_expr" \
        --arg type "$task_type" \
        --arg at "$at" \
        --arg created_at "$created_at" \
        --argjson enabled true \
        --arg working_dir "$working_dir" \
        '{
            id: $id,
            name: $name,
            prompt: $prompt,
            schedule: $schedule,
            type: $type,
            at: $at,
            created_at: $created_at,
            enabled: $enabled,
            working_dir: $working_dir
        }')

    echo "$task_json" > "$TASKS_DIR/${id}.json"
    rebuild_crontab

    echo "Task created:"
    echo "  ID:       $id"
    echo "  Name:     $name"
    echo "  Type:     $task_type"
    if [ "$task_type" = "once" ]; then
        echo "  At:       $at"
    fi
    echo "  Schedule: $cron_expr"
    echo "  Prompt:   $prompt"
}

cmd_remove() {
    local id=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --id) id="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ -z "$id" ]; then
        echo "Error: --id is required" >&2
        return 1
    fi

    local task_file="$TASKS_DIR/${id}.json"
    if [ ! -f "$task_file" ]; then
        echo "Error: task '$id' not found" >&2
        return 1
    fi

    local name
    name=$(jq -r '.name' "$task_file")
    rm -f "$task_file"
    rebuild_crontab
    echo "Removed task '$name' ($id)"
}

cmd_enable() {
    local id=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --id) id="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ -z "$id" ]; then
        echo "Error: --id is required" >&2
        return 1
    fi

    local task_file="$TASKS_DIR/${id}.json"
    if [ ! -f "$task_file" ]; then
        echo "Error: task '$id' not found" >&2
        return 1
    fi

    local tmp
    tmp=$(mktemp)
    jq '.enabled = true' "$task_file" > "$tmp" && mv "$tmp" "$task_file"
    rebuild_crontab

    local name
    name=$(jq -r '.name' "$task_file")
    echo "Enabled task '$name' ($id)"
}

cmd_disable() {
    local id=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --id) id="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ -z "$id" ]; then
        echo "Error: --id is required" >&2
        return 1
    fi

    local task_file="$TASKS_DIR/${id}.json"
    if [ ! -f "$task_file" ]; then
        echo "Error: task '$id' not found" >&2
        return 1
    fi

    local tmp
    tmp=$(mktemp)
    jq '.enabled = false' "$task_file" > "$tmp" && mv "$tmp" "$task_file"
    rebuild_crontab

    local name
    name=$(jq -r '.name' "$task_file")
    echo "Disabled task '$name' ($id)"
}

cmd_list() {
    local found=false
    printf "%-10s %-20s %-10s %-9s %-20s %s\n" "ID" "NAME" "TYPE" "ENABLED" "SCHEDULE" "PROMPT"
    printf "%-10s %-20s %-10s %-9s %-20s %s\n" "──────────" "────────────────────" "──────────" "─────────" "────────────────────" "──────────────────────────────"

    for task_file in "$TASKS_DIR"/*.json; do
        [ -f "$task_file" ] || continue
        found=true
        local id name type enabled schedule at prompt
        id=$(jq -r '.id' "$task_file")
        name=$(jq -r '.name' "$task_file")
        type=$(jq -r '.type' "$task_file")
        enabled=$(jq -r '.enabled' "$task_file")
        schedule=$(jq -r '.schedule' "$task_file")
        at=$(jq -r '.at // ""' "$task_file")
        prompt=$(jq -r '.prompt' "$task_file")

        local display_schedule="$schedule"
        if [ "$type" = "once" ] && [ -n "$at" ]; then
            display_schedule="at $at"
        fi

        # Truncate long fields for display
        [ ${#name} -gt 20 ] && name="${name:0:17}..."
        [ ${#display_schedule} -gt 20 ] && display_schedule="${display_schedule:0:17}..."
        [ ${#prompt} -gt 30 ] && prompt="${prompt:0:27}..."

        printf "%-10s %-20s %-10s %-9s %-20s %s\n" "$id" "$name" "$type" "$enabled" "$display_schedule" "$prompt"
    done

    if [ "$found" = "false" ]; then
        echo "No scheduled tasks."
    fi
}

cmd_logs() {
    local id="" tail_n=50

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --id)   id="$2"; shift 2 ;;
            --tail) tail_n="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ -n "$id" ]; then
        local log_dir="$LOGS_DIR/$id"
        if [ ! -d "$log_dir" ]; then
            echo "No logs found for task '$id'"
            return 0
        fi
        # Show the most recent log file
        local latest
        latest=$(ls -t "$log_dir"/*.log 2>/dev/null | head -1)
        if [ -z "$latest" ]; then
            echo "No logs found for task '$id'"
            return 0
        fi
        echo "=== Latest log for task $id: $(basename "$latest") ==="
        tail -n "$tail_n" "$latest"
    else
        # Show recent logs across all tasks
        local all_logs
        all_logs=$(find "$LOGS_DIR" -name "*.log" -type f 2>/dev/null | sort -r | head -n 10)
        if [ -z "$all_logs" ]; then
            echo "No logs found."
            return 0
        fi
        for log_file in $all_logs; do
            local task_id
            task_id=$(basename "$(dirname "$log_file")")
            echo "=== Task $task_id: $(basename "$log_file") ==="
            tail -n 5 "$log_file"
            echo ""
        done
    fi
}

cmd_run() {
    local id=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --id) id="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ -z "$id" ]; then
        echo "Error: --id is required" >&2
        return 1
    fi

    local task_file="$TASKS_DIR/${id}.json"
    if [ ! -f "$task_file" ]; then
        echo "Error: task '$id' not found" >&2
        return 1
    fi

    local name
    name=$(jq -r '.name' "$task_file")
    echo "Manually triggering task '$name' ($id)..."
    /usr/local/bin/triple-c-task-runner "$id"
}

cmd_notifications() {
    local clear=false

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --clear) clear=true; shift ;;
            *) echo "Unknown option: $1" >&2; return 1 ;;
        esac
    done

    if [ "$clear" = "true" ]; then
        rm -f "$NOTIFICATIONS_DIR"/*.notify
        echo "Notifications cleared."
        return 0
    fi

    local found=false
    for notify_file in $(ls -t "$NOTIFICATIONS_DIR"/*.notify 2>/dev/null); do
        [ -f "$notify_file" ] || continue
        found=true
        cat "$notify_file"
        echo "---"
    done

    if [ "$found" = "false" ]; then
        echo "No notifications."
    fi
}

# ── Main ─────────────────────────────────────────────────────────────────────

ensure_dirs

if [ $# -eq 0 ]; then
    usage
    exit 1
fi

command="$1"
shift

case "$command" in
    add)           cmd_add "$@" ;;
    remove)        cmd_remove "$@" ;;
    enable)        cmd_enable "$@" ;;
    disable)       cmd_disable "$@" ;;
    list)          cmd_list ;;
    logs)          cmd_logs "$@" ;;
    run)           cmd_run "$@" ;;
    notifications) cmd_notifications "$@" ;;
    help|--help|-h) usage ;;
    *)
        echo "Unknown command: $command" >&2
        usage
        exit 1
        ;;
esac
