#!/usr/bin/env bash
# new.sh — date, weather, and customisable RSS feeds
# zero dependencies: bash + curl + standard unix tools only
# works on macOS, Linux, NetBSD
#
# Usage:
#   new.sh              run normally
#   new.sh --config     interactive feed/headline configurator

set -euo pipefail

# ── config location ───────────────────────────────────────────────────────────
CONFIG_DIR="${HOME}/.news"
CONFIG_FILE="${CONFIG_DIR}/config"
mkdir -p "$CONFIG_DIR"
touch "$CONFIG_FILE"

CURL_TIMEOUT=5

# ── colours ───────────────────────────────────────────────────────────────────
if tput colors &>/dev/null && [ "$(tput colors)" -ge 8 ]; then
  C_RESET=$(tput sgr0)
  C_BOLD=$(tput bold)
  C_DIM=$(tput dim 2>/dev/null || printf '')
  C_RED=$(tput setaf 1)
  C_GREEN=$(tput setaf 2)
  C_YELLOW=$(tput setaf 3)
  C_BLUE=$(tput setaf 4)
  C_CYAN=$(tput setaf 6)
  C_WHITE=$(tput setaf 7)
else
  C_RESET='' C_BOLD='' C_DIM=''
  C_RED='' C_GREEN='' C_YELLOW='' C_BLUE='' C_CYAN='' C_WHITE=''
fi

# ── helpers ───────────────────────────────────────────────────────────────────
get_width() { tput cols 2>/dev/null || echo 80; }

hr() {
  local w; w=$(get_width)
  printf '%s' "${C_DIM}"
  printf '─%.0s' $(seq 1 "$w")
  printf '%s\n' "${C_RESET}"
}

section() {
  printf '\n  %s%s%s\n' "${C_BOLD}${C_YELLOW}" "$1" "${C_RESET}"
  hr
}

# ── check for curl ────────────────────────────────────────────────────────────
if ! command -v curl &>/dev/null; then
  printf '%s[!] curl is required but not found%s\n' "${C_RED}" "${C_RESET}" >&2
  exit 1
fi

# ── master feed catalogue ─────────────────────────────────────────────────────
# Format: "KEY|Display Title|URL|filter1|filter2|..."
# KEY is used in the config file to enable feeds and set headline counts.
declare -a CATALOGUE=(
  # ── General / World News
  "bbc|BBC NEWS|http://feeds.bbci.co.uk/news/rss.xml|BBC News"
  "france24|FRANCE 24|https://www.france24.com/en/rss|France 24"
  "npr|NPR NEWS|https://feeds.npr.org/1001/rss.xml|NPR Topics|NPR : National Public Radio"
  "npr_politics|NPR POLITICS|https://feeds.npr.org/1014/rss.xml|NPR Topics|NPR : National Public Radio"
  "reuters|REUTERS|https://feeds.reuters.com/reuters/topNews|Reuters"
  "guardian|THE GUARDIAN|https://www.theguardian.com/world/rss|The Guardian"
  "aljazeera|AL JAZEERA|https://www.aljazeera.com/xml/rss/all.xml|Al Jazeera"
  "dw|DEUTSCHE WELLE|https://rss.dw.com/rdf/rss-en-all|Deutsche Welle"
  "cbc|CBC NEWS|https://www.cbc.ca/cmlink/rss-topstories|CBC News"
  "abc_au|ABC NEWS (AU)|https://www.abc.net.au/news/feed/51120/rss.xml|ABC News"
  "propublica|PROPUBLICA|https://www.propublica.org/feeds/propublica/main|ProPublica"
  "atlantic|THE ATLANTIC|https://www.theatlantic.com/feed/all/|The Atlantic"
  "politico|POLITICO|https://rss.politico.com/politics-news.xml|Politico"
  # ── Tech
  "hn|HACKER NEWS|https://news.ycombinator.com/rss|Hacker News"
  "phoronix|PHORONIX|https://www.phoronix.com/phoronix-rss.php|Phoronix"
  "verge|THE VERGE|https://www.theverge.com/rss/index.xml|The Verge"
  "ars|ARS TECHNICA|https://feeds.arstechnica.com/arstechnica/index|Ars Technica"
  "lobsters|LOBSTE.RS|https://lobste.rs/rss|lobste.rs"
  "slashdot|SLASHDOT|https://rss.slashdot.org/Slashdot/slashdotMain|Slashdot"
  "mit_tech|MIT TECH REVIEW|https://www.technologyreview.com/feed/|MIT Technology Review"
  "wired|WIRED|https://www.wired.com/feed/rss|Wired"
  "techcrunch|TECHCRUNCH|https://techcrunch.com/feed/|TechCrunch"
  "infoq|INFOQ|https://feed.infoq.com/|InfoQ"
  "osnews|OSNEWS|https://www.osnews.com/feed/|OSNews"
  "9to5mac|9TO5MAC|https://9to5mac.com/feed/|9to5Mac"
  # ── Dev / Engineering
  "devto|DEV.TO|https://dev.to/feed|DEV Community"
  "so_blog|STACK OVERFLOW BLOG|https://stackoverflow.blog/feed/|Stack Overflow Blog"
  "gh_blog|GITHUB BLOG|https://github.blog/feed/|GitHub Blog"
  "fowler|MARTIN FOWLER|https://martinfowler.com/feed.atom|Martin Fowler"
  "lwn|LWN.NET|https://lwn.net/headlines/rss|LWN"
  # ── Security / Infosec
  "krebs|KREBS ON SECURITY|https://krebsonsecurity.com/feed/|Krebs on Security"
  "schneier|SCHNEIER ON SECURITY|https://www.schneier.com/feed/atom|Schneier on Security"
  "sans|SANS STORMCAST|https://isc.sans.edu/rssfeed_full.xml|SANS Internet Stormcast"
  # ── Science
  "nasa|NASA|https://www.nasa.gov/rss/dyn/breaking_news.rss|NASA"
  "quanta|QUANTA MAGAZINE|https://www.quantamagazine.org/feed/|Quanta Magazine"
  "sciencedaily|SCIENCE DAILY|https://www.sciencedaily.com/rss/all.xml|Science Daily"
  "space|SPACE.COM|https://www.space.com/feeds/all|Space.com"
  # ── Privacy / FOSS
  "eff|EFF DEEPLINKS|https://www.eff.org/rss/updates.xml|EFF"
  "omgubuntu|OMG UBUNTU|https://www.omgubuntu.co.uk/feed|OMG! Ubuntu"
  "itsfoss|IT'S FOSS|https://itsfoss.com/feed/|It's FOSS"
)

# Default feeds (active when no config exists)
DEFAULT_ACTIVE="bbc france24 npr slashdot"
DEFAULT_HEADLINES=6

# ── config read/write ─────────────────────────────────────────────────────────
cfg_get() {
  # cfg_get KEY default_value
  local key="$1" default="${2:-}"
  local val
  val=$(grep "^${key}=" "$CONFIG_FILE" 2>/dev/null | tail -n1 | cut -d'=' -f2- || echo "")
  printf '%s' "${val:-$default}"
}

cfg_set() {
  local key="$1" val="$2"
  local tmp="${CONFIG_DIR}/.cfg_tmp"
  grep -v "^${key}=" "$CONFIG_FILE" > "$tmp" 2>/dev/null || true
  printf '%s=%s\n' "$key" "$val" >> "$tmp"
  mv "$tmp" "$CONFIG_FILE"
}

# ── catalogue lookup helpers ──────────────────────────────────────────────────
catalogue_field() {
  # catalogue_field KEY FIELD_INDEX  (0=key,1=title,2=url,3+=filters)
  local key="$1" idx="$2"
  local entry
  for entry in "${CATALOGUE[@]}"; do
    local k; k=$(printf '%s' "$entry" | cut -d'|' -f1)
    if [[ "$k" == "$key" ]]; then
      printf '%s' "$entry" | cut -d'|' -f$(( idx + 1 ))
      return
    fi
  done
}

catalogue_filters() {
  local key="$1"
  local entry
  for entry in "${CATALOGUE[@]}"; do
    local k; k=$(printf '%s' "$entry" | cut -d'|' -f1)
    if [[ "$k" == "$key" ]]; then
      # fields 4 onward are filters
      printf '%s' "$entry" | cut -d'|' -f4-
      return
    fi
  done
}

# ── fetch & display a single feed ─────────────────────────────────────────────
fetch_feed() {
  local title="$1" url="$2" max="$3"
  shift 3
  local filter_pat="${*:-XNOMATCHWILLEVEROCCUR}"
  filter_pat=$(printf '%s' "$filter_pat" | tr '|' '\n' | paste -sd '|' -)

  local raw
  raw=$(curl -sL --max-time "$CURL_TIMEOUT" "$url" 2>/dev/null || echo "")

  if [[ -z "$raw" ]]; then
    printf '    %s[feed unreachable]%s\n' "${C_RED}" "${C_RESET}"
    return
  fi

  local items
  items=$(printf '%s' "$raw" | \
    grep -o '<title><!\[CDATA\[[^\]]*\]\]><\/title>\|<title>[^<]*<\/title>' | \
    sed \
      -e 's/<title><!\[CDATA\[//g' \
      -e 's/\]\]><\/title>//g' \
      -e 's/<title>//g' \
      -e 's/<\/title>//g' \
      -e 's/&amp;/\&/g' \
      -e 's/&lt;/</g' \
      -e 's/&gt;/>/g' \
      -e 's/&quot;/"/g' \
      -e "s/&#39;/'/g" \
      -e 's/^[[:space:]]*//' \
      -e '/^[[:space:]]*$/d' | \
    grep -v -E "$filter_pat" | \
    head -"$max" || echo "")

  if [[ -z "$items" ]]; then
    printf '    %s[no items found]%s\n' "${C_RED}" "${C_RESET}"
    return
  fi

  local count=1
  while IFS= read -r line; do
    [[ -z "$line" ]] && continue
    printf '    %s%2d.%s %s\n' "${C_CYAN}" "$count" "${C_RESET}" "$line"
    (( count++ ))
  done <<< "$items"
}

# ══════════════════════════════════════════════════════════════════════════════
# CONFIG MODE
# ══════════════════════════════════════════════════════════════════════════════
run_config() {
  local active_str
  active_str=$(cfg_get "active" "$DEFAULT_ACTIVE")
  local default_headlines
  default_headlines=$(cfg_get "headlines" "$DEFAULT_HEADLINES")

  while true; do
    clear
    printf '\n  %s%snew.sh — configuration%s\n\n' "${C_BOLD}" "${C_WHITE}" "${C_RESET}"

    printf '  %sDefault headlines per feed:%s %s%s%s\n\n' \
      "${C_DIM}" "${C_RESET}" "${C_BOLD}${C_GREEN}" "$default_headlines" "${C_RESET}"

    printf '  %sActive feeds:%s\n' "${C_DIM}" "${C_RESET}"
    local entry key title headlines_key hl active_keys
    IFS=' ' read -ra active_keys <<< "$active_str"

    # group catalogue by category for display
    declare -A CATEGORIES
    local current_cat=''
    for entry in "${CATALOGUE[@]}"; do
      IFS='|' read -r key title url rest <<< "$entry"
      # detect category comment blocks (keys with no URL have category markers)
      local is_active=''
      for ak in "${active_keys[@]}"; do
        [[ "$ak" == "$key" ]] && is_active=1 && break
      done
      headlines_key="hl_${key}"
      hl=$(cfg_get "$headlines_key" "$default_headlines")
      if [[ -n "$is_active" ]]; then
        printf '    %s[ON] %s%s  %s(%s headlines)%s\n' \
          "${C_GREEN}${C_BOLD}" "$title" "${C_RESET}" \
          "${C_DIM}" "$hl" "${C_RESET}"
      else
        printf '    %s[--] %s%s\n' "${C_DIM}" "$title" "${C_RESET}"
      fi
    done

    printf '\n'
    hr
    printf '\n'
    printf '  %s[t]%s toggle a feed on/off\n' "${C_CYAN}" "${C_RESET}"
    printf '  %s[h]%s set headlines for a specific feed\n' "${C_CYAN}" "${C_RESET}"
    printf '  %s[d]%s set default headlines for all feeds\n' "${C_CYAN}" "${C_RESET}"
    printf '  %s[r]%s reset to defaults\n' "${C_CYAN}" "${C_RESET}"
    printf '  %s[q]%s save & quit\n' "${C_CYAN}" "${C_RESET}"
    printf '\n  choice: '
    read -r choice

    case "$choice" in
      t|T)
        printf '\n  Enter feed key to toggle (e.g. bbc, hn, slashdot): '
        read -r toggle_key
        toggle_key=$(printf '%s' "$toggle_key" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
        # validate key exists in catalogue
        local found=0
        for entry in "${CATALOGUE[@]}"; do
          local ek; ek=$(printf '%s' "$entry" | cut -d'|' -f1)
          [[ "$ek" == "$toggle_key" ]] && found=1 && break
        done
        if [[ $found -eq 0 ]]; then
          printf '  %s[!] unknown feed key%s\n' "${C_RED}" "${C_RESET}"
          sleep 1; continue
        fi
        # toggle in/out of active list
        local new_active=''
        local was_active=0
        for ak in "${active_keys[@]}"; do
          if [[ "$ak" == "$toggle_key" ]]; then
            was_active=1
          else
            new_active="${new_active} ${ak}"
          fi
        done
        if [[ $was_active -eq 0 ]]; then
          new_active="${active_str} ${toggle_key}"
        fi
        active_str=$(printf '%s' "$new_active" | xargs)
        IFS=' ' read -ra active_keys <<< "$active_str"
        ;;

      h|H)
        printf '\n  Enter feed key: '
        read -r hl_key
        hl_key=$(printf '%s' "$hl_key" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
        printf '  Headlines to show for %s [1-20]: ' "$hl_key"
        read -r hl_val
        if [[ "$hl_val" =~ ^[0-9]+$ ]] && (( hl_val >= 1 && hl_val <= 20 )); then
          cfg_set "hl_${hl_key}" "$hl_val"
          printf '  %s✓ set%s\n' "${C_GREEN}" "${C_RESET}"
        else
          printf '  %s[!] enter a number between 1 and 20%s\n' "${C_RED}" "${C_RESET}"
        fi
        sleep 1
        ;;

      d|D)
        printf '\n  Default headlines for all feeds [1-20]: '
        read -r dval
        if [[ "$dval" =~ ^[0-9]+$ ]] && (( dval >= 1 && dval <= 20 )); then
          default_headlines="$dval"
          cfg_set "headlines" "$dval"
          printf '  %s✓ saved%s\n' "${C_GREEN}" "${C_RESET}"
        else
          printf '  %s[!] enter a number between 1 and 20%s\n' "${C_RED}" "${C_RESET}"
        fi
        sleep 1
        ;;

      r|R)
        active_str="$DEFAULT_ACTIVE"
        default_headlines="$DEFAULT_HEADLINES"
        cfg_set "active"    "$active_str"
        cfg_set "headlines" "$default_headlines"
        # wipe per-feed overrides
        grep -v '^hl_' "$CONFIG_FILE" > "${CONFIG_DIR}/.cfg_tmp" 2>/dev/null || true
        mv "${CONFIG_DIR}/.cfg_tmp" "$CONFIG_FILE"
        printf '  %s✓ reset to defaults%s\n' "${C_GREEN}" "${C_RESET}"
        sleep 1
        ;;

      q|Q)
        cfg_set "active" "$active_str"
        printf '  %s✓ saved%s\n' "${C_GREEN}" "${C_RESET}"
        sleep 1
        break
        ;;
    esac
  done
}

# ══════════════════════════════════════════════════════════════════════════════
# NEWS MODE
# ══════════════════════════════════════════════════════════════════════════════
run_news() {
  clear

  # ── date & time ──────────────────────────────────────────────────────────────
  printf '\n  %s%s%s   %s%s%s\n' \
    "${C_BOLD}${C_WHITE}" "$(date '+%A, %d %B %Y')" "${C_RESET}" \
    "${C_BOLD}${C_CYAN}"  "$(date '+%H:%M:%S %Z')"  "${C_RESET}"
  hr

  # ── weather ──────────────────────────────────────────────────────────────────
  section "WEATHER"
  printf '    '
  if weather=$(curl -sS --max-time "$CURL_TIMEOUT" "wttr.in/?format=3" 2>/dev/null); then
    printf '%s%s%s\n' "${C_GREEN}" "$weather" "${C_RESET}"
  else
    printf '%s[weather unavailable]%s\n' "${C_RED}" "${C_RESET}"
  fi

  # ── active feeds ─────────────────────────────────────────────────────────────
  local active_str default_headlines
  active_str=$(cfg_get "active" "$DEFAULT_ACTIVE")
  default_headlines=$(cfg_get "headlines" "$DEFAULT_HEADLINES")

  local active_keys
  IFS=' ' read -ra active_keys <<< "$active_str"

  if [[ ${#active_keys[@]} -eq 0 ]]; then
    printf '\n  %s[no feeds enabled — run: new.sh --config]%s\n' "${C_DIM}" "${C_RESET}"
  fi

  for key in "${active_keys[@]}"; do
    [[ -z "$key" ]] && continue
    local title url filters hl
    title=$(catalogue_field "$key" 1)
    url=$(catalogue_field   "$key" 2)
    filters=$(catalogue_filters "$key")
    hl=$(cfg_get "hl_${key}" "$default_headlines")

    [[ -z "$url" ]] && continue

    section "$title"
    # pass filters as separate args
    local filter_args=()
    IFS='|' read -ra filter_args <<< "$filters"
    fetch_feed "$title" "$url" "$hl" "${filter_args[@]}"
  done

  printf '\n'
  hr
  printf '  %srun %snew.sh --config%s%s to toggle feeds and set headline counts%s\n\n' \
    "${C_DIM}" "${C_CYAN}" "${C_RESET}" "${C_DIM}" "${C_RESET}"
}

# ══════════════════════════════════════════════════════════════════════════════
# ENTRY POINT
# ══════════════════════════════════════════════════════════════════════════════
case "${1:-}" in
  --config|-c) run_config ;;
  *)           run_news   ;;
esac
