#!/usr/bin/env bash

if [ -z "${BASH_VERSION:-}" ]; then
  exec bash "$0" "$@"
fi

set -euo pipefail

API_BASE_URL="https://readyforlinux.com"
SUBMIT_URL="${API_BASE_URL}/api/reports/submit"
PUBLIC_BASE_URL="${API_BASE_URL}"

if ! command -v curl >/dev/null 2>&1; then
  echo "curl is required to run this script."
  exit 1
fi

trim() {
  local value="$1"
  value="${value#${value%%[![:space:]]*}}"
  value="${value%${value##*[![:space:]]}}"
  printf '%s' "$value"
}

prompt() {
  local message="$1"
  local result
  read -r -p "$message" result
  printf '%s' "$(trim "$result")"
}

prompt_yes_no() {
  local message="$1"
  local default="$2"
  local suffix="[y/N]"
  if [[ "$default" == "y" ]]; then
    suffix="[Y/n]"
  fi

  while true; do
    local answer
    read -r -p "$message $suffix " answer
    answer="$(trim "$answer" | tr '[:upper:]' '[:lower:]')"

    if [[ -z "$answer" ]]; then
      answer="$default"
    fi

    case "$answer" in
      y|yes) printf 'true'; return ;;
      n|no) printf 'false'; return ;;
      *) echo "Please enter y or n." ;;
    esac
  done
}

format_choice_label() {
  local value="$1"
  value="${value//-/ }"
  printf '%s' "$value"
}

prompt_choice() {
  local message="$1"
  local default_value="$2"
  shift 2
  local options=("$@")
  local default_index=""
  local i

  for i in "${!options[@]}"; do
    if [[ "${options[$i]}" == "$default_value" ]]; then
      default_index="$((i + 1))"
      break
    fi
  done

  while true; do
    echo "$message" >&2
    for i in "${!options[@]}"; do
      local number="$((i + 1))"
      local suffix=""
      if [[ "${options[$i]}" == "$default_value" ]]; then
        suffix=" (default)"
      fi
      echo "  $number) $(format_choice_label "${options[$i]}")$suffix" >&2
    done

    local answer
    read -r -p "Choose an option [${default_index:-$default_value}]: " answer
    answer="$(trim "$answer" | tr '[:upper:]' '[:lower:]')"
    if [[ -z "$answer" ]]; then
      answer="$default_value"
    elif [[ "$answer" =~ ^[0-9]+$ ]]; then
      local selected_index="$((answer - 1))"
      if (( selected_index >= 0 && selected_index < ${#options[@]} )); then
        printf '%s' "${options[$selected_index]}"
        return
      fi
    fi

    for opt in "${options[@]}"; do
      if [[ "$answer" == "$opt" ]]; then
        printf '%s' "$answer"
        return
      fi
    done

    echo "Invalid choice. Enter the option number or one of: ${options[*]}" >&2
  done
}

sanitize_inline() {
  local input="$1"
  input="${input//$'\t'/ }"
  input="${input//$'\n'/ }"
  printf '%s' "$(trim "$input")"
}

parse_csv_to_lines() {
  local csv="$1"
  local -A seen=()
  IFS=',' read -r -a arr <<< "$csv"
  for raw in "${arr[@]}"; do
    local cleaned
    cleaned="$(sanitize_inline "$raw")"
    if [[ -n "$cleaned" && -z "${seen[$cleaned]+x}" ]]; then
      seen["$cleaned"]=1
      printf '%s\n' "$cleaned"
    fi
  done
}

detect_desktop_env() {
  if [[ -n "${XDG_CURRENT_DESKTOP:-}" ]]; then
    printf '%s' "${XDG_CURRENT_DESKTOP}"
    return
  fi
  if [[ -n "${DESKTOP_SESSION:-}" ]]; then
    printf '%s' "${DESKTOP_SESSION}"
    return
  fi
  if command -v loginctl >/dev/null 2>&1 && [[ -n "${XDG_SESSION_ID:-}" ]]; then
    local loginctl_desktop
    loginctl_desktop="$(loginctl show-session "$XDG_SESSION_ID" -p Desktop --value 2>/dev/null || true)"
    loginctl_desktop="$(sanitize_inline "$loginctl_desktop")"
    if [[ -n "$loginctl_desktop" ]]; then
      printf '%s' "$loginctl_desktop"
      return
    fi
  fi
  if pgrep -x gnome-shell >/dev/null 2>&1; then
    printf '%s' "GNOME"
    return
  fi
  if pgrep -x plasmashell >/dev/null 2>&1; then
    printf '%s' "KDE"
    return
  fi
  if pgrep -x xfce4-session >/dev/null 2>&1; then
    printf '%s' "XFCE"
    return
  fi
  printf '%s' "headless"
}

detect_device_type() {
  if compgen -G "/sys/class/power_supply/BAT*" >/dev/null 2>&1; then
    printf '%s' "laptop"
    return
  fi

  local chassis_type
  chassis_type="$(cat /sys/class/dmi/id/chassis_type 2>/dev/null || true)"
  case "$chassis_type" in
    8|9|10|14) printf '%s' "laptop" ;;
    3|4|5|6|7|13|15|16|23|24) printf '%s' "desktop" ;;
    *) printf '%s' "desktop" ;;
  esac
}

detect_gpu_summary() {
  if ! command -v lspci >/dev/null 2>&1; then
    printf '%s' ""
    return
  fi

  # Keep only readable model text, removing PCI slot prefix and trailing revision noise.
  lspci 2>/dev/null \
    | awk '/VGA compatible controller|3D controller|Display controller/ {print}' \
    | head -n 2 \
    | sed -E 's/^[0-9a-fA-F:.]+[[:space:]]+//' \
    | sed -E 's/^(VGA compatible controller|3D controller|Display controller):[[:space:]]*//' \
    | sed -E 's/[[:space:]]*\(rev[[:space:]]+[0-9a-fA-F]+\)$//' \
    | paste -sd '; ' -
}

detect_storage_summary() {
  if ! command -v lsblk >/dev/null 2>&1; then
    printf '%s' ""
    return
  fi

  # Example: nvme0n1 1.8T, sda 931.5G
  lsblk -dn -o NAME,SIZE,TYPE 2>/dev/null \
    | awk '$3=="disk" {print $1 " " $2}' \
    | paste -sd ', ' -
}

read_first_line() {
  local file_path="$1"
  if [[ -r "$file_path" ]]; then
    head -n 1 "$file_path" 2>/dev/null || true
  fi
}

is_placeholder_model() {
  local value
  value="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
  case "$value" in
    ""|"to be filled by o.e.m."|"to be filled by oem"|"system product name"|"default string"|"none")
      return 0
      ;;
    *)
      return 1
      ;;
  esac
}

detect_hardware_model() {
  local vendor product version tree_model combined

  vendor="$(sanitize_inline "$(read_first_line /sys/class/dmi/id/sys_vendor)")"
  product="$(sanitize_inline "$(read_first_line /sys/class/dmi/id/product_name)")"
  version="$(sanitize_inline "$(read_first_line /sys/class/dmi/id/product_version)")"

  if ! is_placeholder_model "$vendor" && ! is_placeholder_model "$product"; then
    combined="$vendor $product"
    if [[ -n "$version" ]] && ! is_placeholder_model "$version"; then
      combined="$combined $version"
    fi
    printf '%s' "$(sanitize_inline "$combined")"
    return
  fi

  if [[ -r /proc/device-tree/model ]]; then
    tree_model="$(tr -d '\000' < /proc/device-tree/model 2>/dev/null || true)"
    tree_model="$(sanitize_inline "$tree_model")"
    if [[ -n "$tree_model" ]]; then
      printf '%s' "$tree_model"
      return
    fi
  fi

  if command -v hostnamectl >/dev/null 2>&1; then
    local host_model
    host_model="$(hostnamectl 2>/dev/null | awk -F: '/Hardware Model/ {print $2; exit}' | xargs || true)"
    host_model="$(sanitize_inline "$host_model")"
    if [[ -n "$host_model" ]]; then
      printf '%s' "$host_model"
      return
    fi
  fi

  printf '%s' ""
}

json_escape() {
  printf '%s' "$1" | sed \
    -e 's/\\/\\\\/g' \
    -e 's/"/\\"/g' \
    -e 's/\t/\\t/g' \
    -e 's/\r/\\r/g' \
    -e ':a;N;$!ba;s/\n/\\n/g'
}

json_string_or_null() {
  local value="$1"
  if [[ -z "$value" ]]; then
    printf 'null'
    return
  fi
  printf '"%s"' "$(json_escape "$value")"
}

json_number_or_null() {
  local value="$1"
  if [[ "$value" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
    printf '%s' "$value"
  else
    printf 'null'
  fi
}

build_json_string_array_from_lines() {
  local file_path="$1"
  local first=true
  local line
  printf '['
  while IFS= read -r line || [[ -n "$line" ]]; do
    line="$(sanitize_inline "$line")"
    [[ -z "$line" ]] && continue
    if [[ "$first" == true ]]; then
      first=false
    else
      printf ','
    fi
    printf '"%s"' "$(json_escape "$line")"
  done < "$file_path"
  printf ']'
}

build_json_items_from_tsv() {
  local file_path="$1"
  local first=true
  local line
  printf '['
  while IFS= read -r line || [[ -n "$line" ]]; do
    [[ -z "$line" ]] && continue
    IFS=$'\t' read -r name usage status notes <<< "$line"
    name="$(sanitize_inline "${name:-}")"
    usage="$(sanitize_inline "${usage:-}")"
    status="$(sanitize_inline "${status:-}")"
    notes="$(sanitize_inline "${notes:-}")"
    [[ -z "$name" ]] && continue

    if [[ "$first" == true ]]; then
      first=false
    else
      printf ','
    fi

    printf '{"name":"%s","usageFrequency":"%s","linuxStatus":"%s","notes":%s}' \
      "$(json_escape "$name")" \
      "$(json_escape "$usage")" \
      "$(json_escape "$status")" \
      "$(json_string_or_null "$notes")"
  done < "$file_path"
  printf ']'
}

extract_json_string_field() {
  local body="$1"
  local key="$2"
  printf '%s' "$body" | sed -nE "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*/\\1/p" | head -n1
}

extract_json_number_field() {
  local body="$1"
  local key="$2"
  printf '%s' "$body" | sed -nE "s/.*\"$key\"[[:space:]]*:[[:space:]]*([0-9]+).*/\\1/p" | head -n1
}

normalize_share_url() {
  local value="$1"
  if [[ -z "$value" ]]; then
    printf '%s' ""
    return
  fi

  if [[ "$value" == /* ]]; then
    printf '%s' "${PUBLIC_BASE_URL}${value}"
    return
  fi

  printf '%s' "$value"
}

distro_name=""
distro_version=""
if [[ -f /etc/os-release ]]; then
  distro_name="$(grep '^NAME=' /etc/os-release | head -n1 | cut -d= -f2- | tr -d '"' || true)"
  distro_version="$(grep '^VERSION=' /etc/os-release | head -n1 | cut -d= -f2- | tr -d '"' || true)"
fi

kernel_version="$(uname -r 2>/dev/null || true)"
desktop_env="$(detect_desktop_env)"
detected_device_type="$(detect_device_type)"
cpu_model="$(lscpu 2>/dev/null | awk -F: '/Model name/ {print $2; exit}' | xargs || true)"
gpu_summary="$(detect_gpu_summary || true)"
ram_gb="$(awk '/MemTotal/ {printf "%.2f", $2/1024/1024}' /proc/meminfo 2>/dev/null || true)"
storage_summary="$(detect_storage_summary || true)"
hardware_model="$(detect_hardware_model || true)"

echo
echo "Detected system profile:"
echo "- Hardware model: $(sanitize_inline "$hardware_model")"
echo "- Distro: $(sanitize_inline "$distro_name") $(sanitize_inline "$distro_version")"
echo "- Kernel: $(sanitize_inline "$kernel_version")"
echo "- Desktop: $(sanitize_inline "$desktop_env")"
echo "- Device type: $detected_device_type"
echo "- CPU: $(sanitize_inline "$cpu_model")"
echo "- GPU: $(sanitize_inline "$gpu_summary")"
echo "- RAM (GB): $(sanitize_inline "$ram_gb")"
echo "- Storage: $(sanitize_inline "$storage_summary")"

use_detected="$(prompt_yes_no "Use this detected hardware profile?" "y")"
share_public="$(prompt_yes_no "Allow public aggregated insights from your report?" "y")"

device_type="$(prompt_choice "Device type (laptop/desktop/handheld/other)" "$detected_device_type" laptop desktop handheld other)"
overall_experience="$(prompt_choice "Overall Linux experience (great/good/mixed/poor)" "good" great good mixed poor)"

declare -a apps_items=()
declare -a games_items=()

collect_items() {
  local kind="$1"
  local -n out_ref="$2"

  local names_csv
  names_csv="$(prompt "Most used ${kind} (comma-separated, blank to skip): ")"
  local item_names=()
  mapfile -t item_names < <(parse_csv_to_lines "$names_csv")

  local item_name
  for item_name in "${item_names[@]}"; do
    [[ -z "$item_name" ]] && continue

    echo
    echo "${kind^}: $item_name"
    local usage
    local status
    local notes

    usage="$(prompt_choice "Usage frequency (daily/weekly/monthly/rarely)" "weekly" daily weekly monthly rarely)"
    status="$(prompt_choice "Linux status (works-great/works-with-tweaks/minor-issues/major-issues/does-not-work/not-tested)" "works-great" works-great works-with-tweaks minor-issues major-issues does-not-work not-tested)"
    notes="$(sanitize_inline "$(prompt "Optional notes: ")")"

    out_ref+=("$(sanitize_inline "$item_name")"$'\t'"$usage"$'\t'"$status"$'\t'"$notes")
  done
}

collect_items "apps" apps_items
collect_items "games" games_items

blockers_csv="$(prompt "Top blockers still hit (comma-separated, optional): ")"
working_well_csv="$(prompt "What works well on Linux for you? (comma-separated, optional): ")"
notes="$(sanitize_inline "$(prompt "Anything else to share? (optional): ")")"

if [[ "$use_detected" == "false" ]]; then
  distro_name="$(sanitize_inline "$(prompt "Distro name: ")")"
  distro_version="$(sanitize_inline "$(prompt "Distro version: ")")"
  kernel_version="$(sanitize_inline "$(prompt "Kernel version: ")")"
  desktop_env="$(sanitize_inline "$(prompt "Desktop environment: ")")"
  cpu_model="$(sanitize_inline "$(prompt "CPU model: ")")"
  gpu_summary="$(sanitize_inline "$(prompt "GPU summary: ")")"
  storage_summary="$(sanitize_inline "$(prompt "Storage summary: ")")"
fi

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT

apps_file="$tmp_dir/apps.tsv"
games_file="$tmp_dir/games.tsv"
blockers_file="$tmp_dir/blockers.txt"
working_file="$tmp_dir/working.txt"

printf '%s\n' "${apps_items[@]:-}" > "$apps_file"
printf '%s\n' "${games_items[@]:-}" > "$games_file"
parse_csv_to_lines "$blockers_csv" > "$blockers_file"
parse_csv_to_lines "$working_well_csv" > "$working_file"

echo
echo "Summary:"
echo "- apps reported: ${#apps_items[@]}"
echo "- games reported: ${#games_items[@]}"
echo "- share publicly: $share_public"

should_submit="$(prompt_yes_no "Submit report to $SUBMIT_URL?" "y")"
if [[ "$should_submit" == "false" ]]; then
  echo "Submission cancelled."
  exit 0
fi

export REPORT_SUBMIT_URL="$SUBMIT_URL"
export REPORT_SHARE_PUBLIC="$share_public"
export REPORT_DISTRO_NAME="$(sanitize_inline "$distro_name")"
export REPORT_DISTRO_VERSION="$(sanitize_inline "$distro_version")"
export REPORT_KERNEL_VERSION="$(sanitize_inline "$kernel_version")"
export REPORT_DESKTOP_ENV="$(sanitize_inline "$desktop_env")"
export REPORT_DEVICE_TYPE="$device_type"
export REPORT_CPU_MODEL="$(sanitize_inline "$cpu_model")"
export REPORT_GPU_SUMMARY="$(sanitize_inline "$gpu_summary")"
export REPORT_RAM_GB="$(sanitize_inline "$ram_gb")"
export REPORT_STORAGE_SUMMARY="$(sanitize_inline "$storage_summary")"
export REPORT_HARDWARE_MODEL="$(sanitize_inline "$hardware_model")"
export REPORT_OVERALL_EXPERIENCE="$overall_experience"
export REPORT_NOTES="$notes"
export REPORT_APPS_FILE="$apps_file"
export REPORT_GAMES_FILE="$games_file"
export REPORT_BLOCKERS_FILE="$blockers_file"
export REPORT_WORKING_FILE="$working_file"

apps_json="$(build_json_items_from_tsv "$REPORT_APPS_FILE")"
games_json="$(build_json_items_from_tsv "$REPORT_GAMES_FILE")"
blockers_json="$(build_json_string_array_from_lines "$REPORT_BLOCKERS_FILE")"
working_json="$(build_json_string_array_from_lines "$REPORT_WORKING_FILE")"
share_public_json='false'
if [[ "$REPORT_SHARE_PUBLIC" == "true" ]]; then
  share_public_json='true'
fi

payload="$(
  cat <<EOF
{
  "sharePublic": $share_public_json,
  "distroName": $(json_string_or_null "$REPORT_DISTRO_NAME"),
  "distroVersion": $(json_string_or_null "$REPORT_DISTRO_VERSION"),
  "kernelVersion": $(json_string_or_null "$REPORT_KERNEL_VERSION"),
  "desktopEnv": $(json_string_or_null "$REPORT_DESKTOP_ENV"),
  "deviceType": $(json_string_or_null "$REPORT_DEVICE_TYPE"),
  "cpuModel": $(json_string_or_null "$REPORT_CPU_MODEL"),
  "gpuSummary": $(json_string_or_null "$REPORT_GPU_SUMMARY"),
  "ramGb": $(json_number_or_null "$REPORT_RAM_GB"),
  "storageSummary": $(json_string_or_null "$REPORT_STORAGE_SUMMARY"),
  "apps": $apps_json,
  "games": $games_json,
  "blockers": $blockers_json,
  "workingWell": $working_json,
  "overallExperience": $(json_string_or_null "$REPORT_OVERALL_EXPERIENCE"),
  "notes": $(json_string_or_null "$REPORT_NOTES"),
  "metadata": {
    "client": "linux-ready-shell",
    "clientVersion": "0.1.1",
    "hardwareModel": $(json_string_or_null "$REPORT_HARDWARE_MODEL")
  }
}
EOF
)"

response_file="$tmp_dir/response.json"
status_code="$(
  curl -sS \
    -o "$response_file" \
    -w "%{http_code}" \
    -X POST \
    -H "Content-Type: application/json" \
    --data "$payload" \
    "$REPORT_SUBMIT_URL"
)"

response_body="$(cat "$response_file" 2>/dev/null || true)"

if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then
  echo "Report submission failed: HTTP $status_code" >&2
  error_field="$(extract_json_string_field "$response_body" "error")"
  detail_field="$(extract_json_string_field "$response_body" "detail")"
  if [[ -n "$error_field" ]]; then
    echo "Reason: $error_field" >&2
  fi
  if [[ -n "$detail_field" ]]; then
    echo "Detail: $detail_field" >&2
  fi
  exit 1
fi

if ! printf '%s' "$response_body" | grep -Eq '"ok"[[:space:]]*:[[:space:]]*true'; then
  echo "Report submission failed: invalid server response" >&2
  exit 1
fi

report_id="$(extract_json_string_field "$response_body" "reportId")"
if [[ -z "$report_id" ]]; then
  report_id="unknown"
fi
echo "Report submitted successfully. Report ID: $report_id"
share_url="$(extract_json_string_field "$response_body" "shareUrl")"
share_url="$(normalize_share_url "$share_url")"
share_token="$(extract_json_string_field "$response_body" "shareToken")"
compatibility_score="$(extract_json_number_field "$response_body" "compatibilityScore")"
if [[ -n "$compatibility_score" ]]; then
  echo "Compatibility score: $compatibility_score"
fi
if [[ -n "$share_token" ]]; then
  share_url="${PUBLIC_BASE_URL}/reports/${share_token}"
fi
if [[ -n "$share_url" ]]; then
  echo "Share card: $share_url"
elif [[ "$share_public" == "true" ]]; then
  echo "Warning: server did not return shareUrl." >&2
  echo "This usually means the deployed API is still on an older version." >&2
  echo "The report ID is not the same as the share token URL segment." >&2
fi
