#!/usr/bin/env bash
set -euo pipefail

BASE_URL="${BASE_URL:-http://172.26.154.109}"
AI_BASE="${AI_BASE:-${BASE_URL%/}/aizhct/index.php}"
MQTT_HOST="${MQTT_HOST:-172.26.154.109}"
MQTT_PORT="${MQTT_PORT:-1883}"
HOST_HEADER="${HOST_HEADER:-}"
DEVICE_CODE="${DEVICE_CODE:-}"
EQUIPMENT_ID="${EQUIPMENT_ID:-}"
MATE_CODE="${MATE_CODE:-}"
DISH_UUID="${DISH_UUID:-}"
FACE_IMAGE="${FACE_IMAGE:-}"
DEPT_UUID="${DEPT_UUID:-}"
PC_ACCOUNT="${PC_ACCOUNT:-admin}"
PC_PASSWORD="${PC_PASSWORD:-}"
MINIAPP_PASSWORD="${MINIAPP_PASSWORD:-}"
ENTERPRISE_NO="${ENTERPRISE_NO:-10051}"
RESTAURANT_ID="${RESTAURANT_ID:-1}"
DISH_WEIGHT="${DISH_WEIGHT:-120}"
HTTP_TIMEOUT="${HTTP_TIMEOUT:-30}"
REPORT_ROOT="${REPORT_ROOT:-./reports}"

require_value() {
  local name="$1"
  local value="$2"
  if [[ -z "$value" ]]; then
    echo "ERROR: $name is required."
    exit 2
  fi
}

read_secret() {
  local prompt="$1"
  local variable_name="$2"
  local value="${!variable_name:-}"
  if [[ -z "$value" && -t 0 ]]; then
    read -rsp "$prompt: " value
    echo
    printf -v "$variable_name" '%s' "$value"
  fi
  require_value "$variable_name" "${!variable_name:-}"
}

require_value "DEVICE_CODE" "$DEVICE_CODE"
require_value "DISH_UUID" "$DISH_UUID"
require_value "FACE_IMAGE" "$FACE_IMAGE"
[[ -f "$FACE_IMAGE" ]] || {
  echo "ERROR: FACE_IMAGE does not exist: $FACE_IMAGE"
  exit 2
}
read_secret "PC administrator password" "PC_PASSWORD"
read_secret "Miniapp initial password" "MINIAPP_PASSWORD"

for command_name in curl python3; do
  command -v "$command_name" >/dev/null 2>&1 || {
    echo "ERROR: $command_name is required."
    exit 2
  }
done

RUN_ID="$(date +%Y%m%d-%H%M%S)"
REPORT_DIR="${REPORT_ROOT%/}/device-full-flow-${RUN_ID}"
mkdir -p "$REPORT_DIR"
MOBILE="139$(date +%s | tail -c 9)"
STAFF_NAME="现场全流程_${RUN_ID}"
PERSON_NO="FLOW${RUN_ID//-/}"
PLATE_CODE="FT$(date +%m%d%H%M%S)"
TODAY="$(date +%F)"
PASS_COUNT=0
FAIL_COUNT=0

record_result() {
  local name="$1"
  local status="$2"
  local detail="$3"
  printf '%s\t%s\t%s\n' "$name" "$status" "$detail" | tee -a "$REPORT_DIR/results.tsv"
  if [[ "$status" == "PASS" ]]; then
    PASS_COUNT=$((PASS_COUNT + 1))
  else
    FAIL_COUNT=$((FAIL_COUNT + 1))
  fi
}

json_value() {
  local file="$1"
  local expression="$2"
  python3 - "$file" "$expression" <<'PY'
import json
import sys

with open(sys.argv[1], "r", encoding="utf-8") as handle:
    data = json.load(handle)

value = data
for part in sys.argv[2].split("."):
    if not part:
        continue
    if isinstance(value, list):
        value = value[int(part)]
    else:
        value = value.get(part)
    if value is None:
        break

if isinstance(value, (dict, list)):
    print(json.dumps(value, ensure_ascii=False))
elif value is not None:
    print(value)
PY
}

redact_secrets() {
  local file="$1"
  python3 - "$file" <<'PY'
import json
import sys

path = sys.argv[1]
with open(path, "r", encoding="utf-8") as handle:
    payload = json.load(handle)

def redact(value):
    if isinstance(value, dict):
        return {
            key: "***REDACTED***" if key.lower() in {"token", "_token"} else redact(item)
            for key, item in value.items()
        }
    if isinstance(value, list):
        return [redact(item) for item in value]
    return value

with open(path, "w", encoding="utf-8") as handle:
    json.dump(redact(payload), handle, ensure_ascii=False)
    handle.write("\n")
PY
}

post_api() {
  local name="$1"
  local url="$2"
  shift 2
  local body_file="$REPORT_DIR/${name}.json"
  local http_file="$REPORT_DIR/${name}.http"
  local args=(
    --noproxy '*'
    -sS
    --connect-timeout 5
    --max-time "$HTTP_TIMEOUT"
    -X POST
    "$url"
    -o "$body_file"
    -w '%{http_code}'
  )
  if [[ -n "$HOST_HEADER" ]]; then
    args+=(-H "Host: $HOST_HEADER")
  fi
  args+=("$@")

  if ! curl "${args[@]}" >"$http_file"; then
    record_result "$name" "FAIL" "curl_error"
    return 1
  fi

  local http_code api_code
  http_code="$(cat "$http_file")"
  api_code="$(python3 - "$body_file" <<'PY'
import json
import sys

try:
    with open(sys.argv[1], "r", encoding="utf-8") as handle:
        payload = json.load(handle)
    print(payload.get("code", payload.get("status", "")))
except Exception:
    print("")
PY
)"
  if [[ "$http_code" =~ ^2[0-9][0-9]$ && ( "$api_code" == "0" || "$api_code" == "200" ) ]]; then
    record_result "$name" "PASS" "HTTP=$http_code API=$api_code"
    return 0
  fi
  record_result "$name" "FAIL" "HTTP=$http_code API=${api_code:-invalid_json}"
  return 1
}

check_tcp() {
  local name="$1"
  local host="$2"
  local port="$3"
  if HOST="$host" PORT="$port" TIMEOUT="$HTTP_TIMEOUT" python3 - <<'PY'
import os
import socket

with socket.create_connection(
    (os.environ["HOST"], int(os.environ["PORT"])),
    timeout=float(os.environ["TIMEOUT"]),
):
    pass
PY
  then
    record_result "$name" "PASS" "$host:$port"
  else
    record_result "$name" "FAIL" "$host:$port"
  fi
}

mqtt_connect_check() {
  if MQTT_HOST="$MQTT_HOST" MQTT_PORT="$MQTT_PORT" \
    MQTT_USERNAME="${MQTT_USERNAME:-}" MQTT_PASSWORD="${MQTT_PASSWORD:-}" \
    MQTT_CLIENT_ID="csfzx-flow-${DEVICE_CODE}-${RUN_ID}" \
    TIMEOUT="$HTTP_TIMEOUT" python3 - <<'PY'
import os
import socket
import struct

def mqtt_string(value):
    raw = value.encode("utf-8")
    return struct.pack("!H", len(raw)) + raw

def remaining_length(value):
    encoded = bytearray()
    while True:
        digit = value % 128
        value //= 128
        if value:
            digit |= 0x80
        encoded.append(digit)
        if not value:
            return bytes(encoded)

username = os.environ.get("MQTT_USERNAME", "")
password = os.environ.get("MQTT_PASSWORD", "")
flags = 0x02
payload = mqtt_string(os.environ["MQTT_CLIENT_ID"])
if username:
    flags |= 0x80
    payload += mqtt_string(username)
if password:
    flags |= 0x40
    payload += mqtt_string(password)
header = mqtt_string("MQTT") + b"\x04" + bytes([flags]) + struct.pack("!H", 30)
body = header + payload
packet = b"\x10" + remaining_length(len(body)) + body
with socket.create_connection(
    (os.environ["MQTT_HOST"], int(os.environ["MQTT_PORT"])),
    timeout=float(os.environ["TIMEOUT"]),
) as sock:
    sock.sendall(packet)
    response = sock.recv(4)
if len(response) < 4 or response[0] != 0x20 or response[3] != 0:
    raise SystemExit("MQTT CONNACK failed: %r" % (response,))
PY
  then
    record_result "mqtt_connect" "PASS" "$MQTT_HOST:$MQTT_PORT"
  else
    record_result "mqtt_connect" "FAIL" "$MQTT_HOST:$MQTT_PORT"
  fi
}

find_department_uuid() {
  python3 - "$REPORT_DIR/department_tree.json" <<'PY'
import json
import sys

with open(sys.argv[1], "r", encoding="utf-8") as handle:
    payload = json.load(handle)

def find_leaf(nodes):
    for node in nodes or []:
        children = node.get("children") or []
        if children:
            result = find_leaf(children)
            if result:
                return result
        elif node.get("uuid"):
            return node["uuid"]
    return ""

print(find_leaf(payload.get("data", {}).get("department", [])))
PY
}

find_equipment_id() {
  DEVICE_CODE="$DEVICE_CODE" python3 - "$REPORT_DIR/equipment_list.json" <<'PY'
import json
import os
import sys

with open(sys.argv[1], "r", encoding="utf-8") as handle:
    payload = json.load(handle)
for row in payload.get("data", {}).get("rows", []):
    if str(row.get("code", "")) == os.environ["DEVICE_CODE"]:
        print(row.get("id", ""))
        break
PY
}

wait_for_order() {
  local channel="$1"
  local url="$2"
  shift 2
  local output="$REPORT_DIR/${channel}_order_list.json"
  local attempt order_id
  for attempt in 1 2 3 4 5 6 7 8 9 10; do
    if [[ -n "$HOST_HEADER" ]]; then
      curl --noproxy '*' -sS --connect-timeout 5 --max-time "$HTTP_TIMEOUT" \
        -X POST "$url" -H "Host: $HOST_HEADER" "$@" -o "$output"
    else
      curl --noproxy '*' -sS --connect-timeout 5 --max-time "$HTTP_TIMEOUT" \
        -X POST "$url" "$@" -o "$output"
    fi
    if [[ "$channel" == "pc" ]]; then
      order_id="$(json_value "$output" "data.rows.0.id" 2>/dev/null || true)"
    else
      order_id="$(json_value "$output" "data.list.data.0.id" 2>/dev/null || true)"
    fi
    if [[ -n "$order_id" ]]; then
      printf '%s' "$order_id"
      return 0
    fi
    sleep 2
  done
  return 1
}

printf 'test\tstatus\tdetail\n' >"$REPORT_DIR/results.tsv"
{
  echo "RUN_ID=$RUN_ID"
  echo "BASE_URL=$BASE_URL"
  echo "AI_BASE=$AI_BASE"
  echo "MQTT_ENDPOINT=$MQTT_HOST:$MQTT_PORT"
  echo "DEVICE_CODE=$DEVICE_CODE"
  echo "MOBILE=$MOBILE"
  echo "STAFF_NAME=$STAFF_NAME"
  echo "PLATE_CODE=$PLATE_CODE"
} >"$REPORT_DIR/environment.txt"

echo "Report: $REPORT_DIR"
check_tcp "http_tcp" "$(BASE_URL="$BASE_URL" python3 -c 'import os,urllib.parse; print(urllib.parse.urlparse(os.environ["BASE_URL"]).hostname)')" \
  "$(BASE_URL="$BASE_URL" python3 -c 'import os,urllib.parse; u=urllib.parse.urlparse(os.environ["BASE_URL"]); print(u.port or (443 if u.scheme == "https" else 80))')"
check_tcp "mqtt_tcp" "$MQTT_HOST" "$MQTT_PORT"
mqtt_connect_check

post_api "pc_login" "${BASE_URL%/}/p/login/login" \
  --data-urlencode "account=$PC_ACCOUNT" \
  --data-urlencode "pwd=$PC_PASSWORD"
PC_TOKEN="$(json_value "$REPORT_DIR/pc_login.json" "data._token")"
require_value "PC_TOKEN" "$PC_TOKEN"
redact_secrets "$REPORT_DIR/pc_login.json"
PC_COOKIE_TOKEN="$(TOKEN="$PC_TOKEN" python3 -c 'import os,urllib.parse; print(urllib.parse.quote(os.environ["TOKEN"], safe=""))')"

if [[ -z "$DEPT_UUID" ]]; then
  post_api "department_tree" "${BASE_URL%/}/p/staff/tree" \
    -H "Cookie: _token=$PC_COOKIE_TOKEN"
  DEPT_UUID="$(find_department_uuid)"
fi
require_value "DEPT_UUID" "$DEPT_UUID"

post_api "create_staff" "${BASE_URL%/}/p/staff/add" \
  -H "Cookie: _token=$PC_COOKIE_TOKEN" \
  --data-urlencode "name=$STAFF_NAME" \
  --data-urlencode "sex=1" \
  --data-urlencode "height=170" \
  --data-urlencode "weight=60" \
  --data-urlencode "birthday=1990-01-01" \
  --data-urlencode "mobile=$MOBILE" \
  --data-urlencode "department_uuid=$DEPT_UUID" \
  --data-urlencode "restaurant_id=$RESTAURANT_ID" \
  --data-urlencode "job_status=1" \
  --data-urlencode "identity_category_id=1" \
  --data-urlencode "identity_number=" \
  --data-urlencode "personnel_umber=$PERSON_NO"
STAFF_UUID="$(json_value "$REPORT_DIR/create_staff.json" "data.uuid")"
require_value "STAFF_UUID" "$STAFF_UUID"

post_api "upload_face" "${BASE_URL%/}/m/Staff/saveFaceByMobile" \
  -F "mobile=$MOBILE" \
  -F "face_img=@$FACE_IMAGE"

if [[ -n "$MATE_CODE" ]]; then
  post_api "device_pair" "${BASE_URL%/}/p/api/matecode" \
    --data-urlencode "matecode=$MATE_CODE"
fi
post_api "equipment_start" "${BASE_URL%/}/p/api/equipmentStart" \
  --data-urlencode "code=$DEVICE_CODE"
post_api "heartbeat" "${BASE_URL%/}/p/api/heartbeat" \
  --data-urlencode "code=$DEVICE_CODE"
post_api "user_sync" "${BASE_URL%/}/p/api/userdata" \
  --data-urlencode "code=$DEVICE_CODE" \
  --data-urlencode "type=0"

if [[ -z "$EQUIPMENT_ID" ]]; then
  post_api "equipment_list" "${BASE_URL%/}/p/Equipment/pushList" \
    -H "Cookie: _token=$PC_COOKIE_TOKEN" \
    --data-urlencode "page=1" \
    --data-urlencode "pageSize=200"
  EQUIPMENT_ID="$(find_equipment_id)"
fi
require_value "EQUIPMENT_ID" "$EQUIPMENT_ID"

post_api "remote_push_dishes" "${BASE_URL%/}/p/Equipment/pushDishes" \
  -H "Cookie: _token=$PC_COOKIE_TOKEN" \
  --data-urlencode "equipment_id=$EQUIPMENT_ID" \
  --data-urlencode "left_dishes_uuid=$DISH_UUID" \
  --data-urlencode "right_dishes_uuid=$DISH_UUID"

DISHES_JSON="$(DISH_UUID="$DISH_UUID" python3 -c 'import json,os; print(json.dumps([os.environ["DISH_UUID"]]))')"
post_api "bind_plate" "${BASE_URL%/}/p/api/bindPlateInfo" \
  --data-urlencode "staff_uuid=$STAFF_UUID" \
  --data-urlencode "plate_code=$PLATE_CODE" \
  --data-urlencode "equipment_code=$DEVICE_CODE" \
  --data-urlencode "dishes_uuids=$DISHES_JSON"

sleep 1
DISH_INFO="$(DISH_UUID="$DISH_UUID" DISH_WEIGHT="$DISH_WEIGHT" python3 - <<'PY'
import datetime
import json
import os

print(json.dumps({
    "create_date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
    "details": [{
        "uuid": os.environ["DISH_UUID"],
        "weight": float(os.environ["DISH_WEIGHT"]),
    }],
}, ensure_ascii=False))
PY
)"
post_api "push_meal" "${BASE_URL%/}/p/api/zhctPushMeal" \
  --data-urlencode "dish_info=$DISH_INFO" \
  --data-urlencode "code=$PLATE_CODE" \
  --data-urlencode "equipment_code=$DEVICE_CODE"

if PC_ORDER_ID="$(wait_for_order "pc" "${BASE_URL%/}/p/mealOrder/list" \
  -H "Cookie: _token=$PC_COOKIE_TOKEN" \
  --data-urlencode "start_date=$TODAY" \
  --data-urlencode "end_date=$TODAY" \
  --data-urlencode "mobile=$MOBILE" \
  --data-urlencode "source=1")"; then
  record_result "pc_order_list" "PASS" "ORDER_ID=$PC_ORDER_ID"
else
  record_result "pc_order_list" "FAIL" "order_not_found"
  exit 1
fi
post_api "pc_order_detail" "${BASE_URL%/}/p/mealOrder/detail?orderId=$PC_ORDER_ID" \
  -H "Cookie: _token=$PC_COOKIE_TOKEN"

post_api "miniapp_login" "${AI_BASE}?s=/api/passport/loginAccount" \
  --data-urlencode "form[account]=$MOBILE" \
  --data-urlencode "form[password]=$MINIAPP_PASSWORD" \
  --data-urlencode "form[enterprise_no]=$ENTERPRISE_NO"
ACCESS_TOKEN="$(json_value "$REPORT_DIR/miniapp_login.json" "data.token")"
require_value "ACCESS_TOKEN" "$ACCESS_TOKEN"
redact_secrets "$REPORT_DIR/miniapp_login.json"
AI_HEADERS=(
  -H "Access-Token: $ACCESS_TOKEN"
  -H "platform: MP-WEIXIN"
  -H "env-version: release"
  -H "version: 1.0.0"
)

if MINI_ORDER_ID="$(wait_for_order "miniapp" "${AI_BASE}?s=/api/mealOrder/list" \
  "${AI_HEADERS[@]}" \
  --data-urlencode "start_date=$TODAY" \
  --data-urlencode "end_date=$TODAY")"; then
  record_result "miniapp_order_list" "PASS" "ORDER_ID=$MINI_ORDER_ID"
else
  record_result "miniapp_order_list" "FAIL" "order_not_found"
  exit 1
fi
post_api "miniapp_order_detail" "${AI_BASE}?s=/api/mealOrder/detail&orderId=$MINI_ORDER_ID" \
  "${AI_HEADERS[@]}"

if [[ "$PC_ORDER_ID" == "$MINI_ORDER_ID" ]]; then
  record_result "order_consistency" "PASS" "ORDER_ID=$PC_ORDER_ID"
else
  record_result "order_consistency" "FAIL" "PC=$PC_ORDER_ID MINIAPP=$MINI_ORDER_ID"
fi

{
  echo "PASS_COUNT=$PASS_COUNT"
  echo "FAIL_COUNT=$FAIL_COUNT"
  echo "MOBILE=$MOBILE"
  echo "STAFF_UUID=$STAFF_UUID"
  echo "EQUIPMENT_ID=$EQUIPMENT_ID"
  echo "PLATE_CODE=$PLATE_CODE"
  echo "ORDER_ID=$PC_ORDER_ID"
  echo "RESULT=$([[ "$FAIL_COUNT" -eq 0 ]] && echo PASS || echo FAIL)"
} | tee "$REPORT_DIR/summary.txt"

[[ "$FAIL_COUNT" -eq 0 ]]
