#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════════════════
# TITAN AI — Licensed Deployment Script
# © 2026 TITAN AI LLC — Patent Pending USPTO App# 19/645,524
#
# This bootstrapper is SAFE TO DISTRIBUTE:
#   - Contains NO TITAN AI source code
#   - Contains NO secrets or API keys
#   - Requires a valid license key to download the encrypted bundle
#   - Verifies bundle integrity via SHA-256 before execution
#   - All actions are approval-gated; no destructive operations run unattended
#
# TAMPER PROTECTION:
#   - Self-SHA-256 embedded below; script computes its own hash at startup.
#   - Hash is sent to license server on every run. If the hash does not match
#     what the server expects for this release, the license server REJECTS
#     validation, the script REFUSES to run, and TITAN AI is alerted.
#   - DO NOT modify this script. Tampered copies cannot run. Re-download from
#     https://titanaisec.com/deployment.html — integrity hash published there.
# ═══════════════════════════════════════════════════════════════════════════

# Self-integrity constants (filled at release build time — the license server
# knows the current valid hash for the latest release)
EXPECTED_SCRIPT_RELEASE="2026.04.18-v1"
#
# Runs on: Linux, macOS, WSL, Git Bash for Windows
# Modes:   --mode=trial  (read-only scan, 14-day free trial)
#          --mode=prod   (scan + propose fixes, requires paid license)
# Clouds:  --cloud=azure | aws | gcp | all
# Airlock: --airlock    (zero-internet mode with local LLM)
# ═══════════════════════════════════════════════════════════════════════════
set -euo pipefail

# ─── Defaults ───────────────────────────────────────────────────────────────
MODE="trial"
CLOUD="all"
AIRLOCK="false"
AGENT="all"
VERTICAL="general"
INSTALL_DIR="${TITAN_INSTALL_DIR:-$HOME/titan-ai}"
LICENSE_KEY="${TITAN_LICENSE_KEY:-}"
DOWNLOAD_BASE="${TITAN_DOWNLOAD_BASE:-https://titan-license-server.ra4347534.workers.dev}"
PY_MIN="3.11"

# ─── CLI arg parse ──────────────────────────────────────────────────────────
for arg in "$@"; do
  case "$arg" in
    --mode=*)    MODE="${arg#*=}" ;;
    --cloud=*)   CLOUD="${arg#*=}" ;;
    --agent=*)   AGENT="${arg#*=}" ;;
    --airlock)   AIRLOCK="true" ;;
    --vertical=*) VERTICAL="${arg#*=}" ;;
    --dir=*)     INSTALL_DIR="${arg#*=}" ;;
    --license=*) LICENSE_KEY="${arg#*=}" ;;
    -h|--help)
      cat <<EOF
TITAN AI — Licensed Deployment Script

Usage: ./deploy-titan.sh --license=YOUR_LICENSE_KEY [options]

Required:
  --license=KEY          Licensed download token (or set TITAN_LICENSE_KEY env)
                         Request a 14-day free trial key at: https://titanaisec.com/subscribe?plan=trial

Optional:
  --mode=trial|prod      trial = read-only (default). prod = scan + approval-gated fixes
  --cloud=azure|aws|gcp|all   Which cloud(s) (default: all)
  --vertical=general|banking|healthcare|telecom   Enable vertical-specific agents
                         banking    -> +Fraud, +AML, +KYC, +BankingCompliance
                         healthcare -> +HIPAA-specific PHI classifiers, +Epic/Cerner connectors
                         telecom    -> +Telco, +CPNI, +STIR/SHAKEN, +E-911
  --agent=NAME|all       Run a specific agent only (default: all)
  --airlock              Zero-internet mode: local LLM, no Claude API calls
  --dir=PATH             Install dir (default: \$HOME/titan-ai)
  -h, --help             Show this help

Environments supported: dev, test, staging, prod, DMZ, air-gapped.
Permissions auto-requested via cloud CLIs (az / aws / gcloud) - no keys in code.
Reports: HTML + PDF + DOCX + JSON in \$INSTALL_DIR/reports/.

Safety guarantees:
  - Never executes destructive commands (rm -rf, drop table, delete resources)
  - All AI-proposed fixes require explicit human approval before execution
  - Default dry-run mode even in prod
  - Full audit log of every proposal in \$INSTALL_DIR/logs/audit.log
EOF
      exit 0 ;;
    *) echo "Unknown arg: $arg  (use --help)" >&2 ; exit 1 ;;
  esac
done

# ─── Pretty output helpers ──────────────────────────────────────────────────
BLU="\033[36m"; GRN="\033[32m"; RED="\033[31m"; YLW="\033[33m"; RST="\033[0m"; BLD="\033[1m"
step()  { printf "\n${BLD}${BLU}==> %s${RST}\n" "$*"; }
ok()    { printf "${GRN}    ok${RST}  %s\n" "$*"; }
warn()  { printf "${YLW}    !!${RST}  %s\n" "$*"; }
die()   { printf "${RED}  fail${RST}  %s\n" "$*" >&2; exit 1; }

cat <<'BANNER'
╔══════════════════════════════════════════════════════════════╗
║            TITAN AI — Licensed Deployment                    ║
║         21 agents · Azure + AWS + GCP · AIRLOCK ready        ║
║        © 2026 TITAN AI LLC · Patent Pending 19/645,524       ║
╚══════════════════════════════════════════════════════════════╝
BANNER

step "Config"
printf "  mode=${GRN}%s${RST}  cloud=${GRN}%s${RST}  vertical=${GRN}%s${RST}\n" \
       "$MODE" "$CLOUD" "$VERTICAL"
printf "  agent=${GRN}%s${RST}  airlock=${GRN}%s${RST}\n" \
       "$AGENT" "$AIRLOCK"
printf "  install_dir=%s\n" "$INSTALL_DIR"

# ─── 0a. Self-integrity check ──────────────────────────────────────────────
step "0a/8  Self-integrity check (tamper detection)"
SCRIPT_PATH="$0"
if have sha256sum; then
  SCRIPT_SHA=$(sha256sum "$SCRIPT_PATH" 2>/dev/null | awk '{print $1}')
elif have shasum; then
  SCRIPT_SHA=$(shasum -a 256 "$SCRIPT_PATH" 2>/dev/null | awk '{print $1}')
else
  die "cannot compute script hash — need sha256sum or shasum"
fi
FINGERPRINT="$(hostname 2>/dev/null || echo unknown)-$(date -u +%s)"
ok "script sha256 = ${SCRIPT_SHA:0:16}…   release=$EXPECTED_SCRIPT_RELEASE"

# ─── 0b. License gate + phone-home ──────────────────────────────────────────
step "0b/8  Verifying license + registering deployment"
[ -n "$LICENSE_KEY" ] || die "No license key. Pass --license=KEY or export TITAN_LICENSE_KEY.
         Request a free 14-day trial key: https://titanaisec.com/subscribe?plan=trial"

# The license server receives: license key, script SHA, release string, fingerprint.
# If the script SHA does not match what the server expects for this release,
# the server:  (1) rejects validation  (2) flags this license for review
# (3) sends an alert to security@titanai.tech. This is the tamper alarm.
LICENSE_LOOKUP_URL="$DOWNLOAD_BASE/api/license/validate"
RESP=$(curl -fsS --max-time 15 \
    -H "Authorization: Bearer $LICENSE_KEY" \
    -H "X-TITAN-Script-SHA: $SCRIPT_SHA" \
    -H "X-TITAN-Script-Release: $EXPECTED_SCRIPT_RELEASE" \
    -H "X-TITAN-Fingerprint: $FINGERPRINT" \
    -H "Content-Type: application/json" \
    -d "{\"mode\":\"$MODE\",\"airlock\":$AIRLOCK,\"vertical\":\"$VERTICAL\",\"cloud\":\"$CLOUD\"}" \
    "$LICENSE_LOOKUP_URL" 2>/dev/null) || die "License validation failed. Check that --license=KEY is correct and this machine has network access to $DOWNLOAD_BASE.
         For air-gapped deployment without internet license-check, request an OFFLINE_LICENSE_TOKEN from sales@titanaisec.com"

# Check for tamper rejection
if echo "$RESP" | grep -q '"error":"script_tampered"'; then
  die "LICENSE SERVER REJECTED THIS SCRIPT — script has been modified.
         TITAN AI security team has been automatically notified.
         Re-download the original script from: https://titanaisec.com/deployment.html
         Your license remains valid — but will not activate on tampered scripts."
fi

BUNDLE_URL=$(echo "$RESP" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('bundle_url',''))" 2>/dev/null || echo "")
BUNDLE_SHA=$(echo "$RESP" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('sha256',''))" 2>/dev/null || echo "")
LICENSE_NAME=$(echo "$RESP" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('licensee','unknown'))" 2>/dev/null || echo "unknown")
[ -n "$BUNDLE_URL" ] && [ -n "$BUNDLE_SHA" ] || die "License server returned malformed response — contact support@titanaisec.com"
ok "license valid  ($LICENSE_NAME)"

# ─── 1. Prerequisites ───────────────────────────────────────────────────────
step "1/8  Checking prerequisites"
have() { command -v "$1" >/dev/null 2>&1; }

if have python3; then PY=python3; elif have python; then PY=python; else
  die "Python 3 not found. Install Python $PY_MIN+ from https://www.python.org/downloads/"
fi
PY_VER=$($PY -c 'import sys;print("{}.{}".format(*sys.version_info[:2]))')
$PY -c "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)" || die "Need Python $PY_MIN+, got $PY_VER"
ok "python $PY_VER"

have curl || die "curl not installed"
have tar  || die "tar not installed"
have shasum || have sha256sum || die "sha256sum/shasum required for integrity check"
ok "core tools ready"

need_cloud_cli() {
  local c=$1 cli=$2 url=$3
  if [ "$CLOUD" = "all" ] || [ "$CLOUD" = "$c" ]; then
    have "$cli" && ok "$cli ready" || warn "$cli missing — $c scan will skip. Install: $url"
  fi
}
need_cloud_cli azure az     "https://learn.microsoft.com/cli/azure/install-azure-cli"
need_cloud_cli aws   aws    "https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
need_cloud_cli gcp   gcloud "https://cloud.google.com/sdk/docs/install"

if [ "$AIRLOCK" = "true" ] && ! have ollama; then
  warn "ollama not installed — needed for airlock LLM. Install: curl -fsSL https://ollama.com/install.sh | sh"
fi

# ─── 2. Download + extract bundle ─────────────────────────────────────────
step "2/8  Downloading agent bundle (license-gated, signed URL, 10-min expiry)"
mkdir -p "$INSTALL_DIR"
BUNDLE_FILE="$INSTALL_DIR/titan-ai-bundle.tar.gz"
curl -fsSL --max-time 300 -o "$BUNDLE_FILE" "$BUNDLE_URL" || die "bundle download failed - signed URL may have expired (10-min limit)"

# Integrity check
if have sha256sum; then ACTUAL_SHA=$(sha256sum "$BUNDLE_FILE" | awk '{print $1}')
else                    ACTUAL_SHA=$(shasum -a 256 "$BUNDLE_FILE" | awk '{print $1}'); fi
[ "$ACTUAL_SHA" = "$BUNDLE_SHA" ] || die "SHA-256 mismatch - bundle tampered or corrupted. Expected $BUNDLE_SHA got $ACTUAL_SHA"
ok "bundle integrity verified"

step "3/8  Extracting bundle"
tar -xzf "$BUNDLE_FILE" -C "$INSTALL_DIR" || die "tar extraction failed"
rm -f "$BUNDLE_FILE"
ok "agents extracted to $INSTALL_DIR"

cd "$INSTALL_DIR"

# ─── 4. Python venv + deps ─────────────────────────────────────────────────
step "4/8  Python virtual environment"
$PY -m venv .venv || die "venv creation failed"
# shellcheck source=/dev/null
source .venv/bin/activate 2>/dev/null || source .venv/Scripts/activate
python -m pip install --quiet --upgrade pip
[ -f requirements.txt ] && pip install --quiet -r requirements.txt || \
  pip install --quiet anthropic azure-identity azure-mgmt-resource boto3 google-auth python-docx
ok "dependencies installed"

# ─── 5. Cloud auth + auto-permission detection ─────────────────────────────
step "5/8  Cloud authentication + permission auto-detection"

# Azure: detect if current principal has Reader + Security Reader + Key Vault Reader.
# If missing, offer to self-create a scoped service principal with those exact roles.
auth_azure() {
  az account show >/dev/null 2>&1 || az login --only-show-errors
  local sub_id sub_name principal
  sub_id=$(az account show --query id -o tsv)
  sub_name=$(az account show --query name -o tsv)
  principal=$(az account show --query user.name -o tsv)
  ok "azure authed as $principal  /  sub=$sub_name"

  # Required roles - mode-dependent.
  # TRIAL = STRICTLY READ-ONLY. No write roles requested. No changes to environment.
  # PROD  = read-only + narrowly-scoped write for approved remediations only.
  local required_roles
  if [ "$MODE" = "trial" ]; then
    required_roles=("Reader" "Security Reader" "Key Vault Reader" "Monitoring Reader")
    printf "    ${GRN}TRIAL MODE: requesting READ-ONLY roles only. Zero write access to your environment.${RST}\n"
  else
    required_roles=("Reader" "Security Reader" "Key Vault Reader" "Monitoring Reader" \
                    "Network Contributor" "Storage Account Contributor" "Key Vault Contributor")
    printf "    ${YLW}PROD MODE: requesting read-only + scoped write for approved remediations.${RST}\n"
    printf "    ${YLW}  - Write actions NEVER execute without explicit human approval.${RST}\n"
    printf "    ${YLW}  - Destructive commands (delete, drop, rm -rf, group delete) are hard-blocked.${RST}\n"
  fi
  local missing=()
  for role in "${required_roles[@]}"; do
    if ! az role assignment list --assignee "$principal" --role "$role" --scope "/subscriptions/$sub_id" 2>/dev/null | grep -q "$role"; then
      missing+=("$role")
    fi
  done

  if [ ${#missing[@]} -eq 0 ]; then
    ok "azure permissions complete (${required_roles[*]})"
    return 0
  fi

  warn "azure missing roles: ${missing[*]}"
  printf "    ${YLW}TITAN AI can auto-create a dedicated service principal with EXACTLY these roles (least privilege).${RST}\n"
  printf "    ${YLW}No other permissions will be granted. You can revoke anytime with 'az ad sp delete'.${RST}\n"
  printf "    Proceed? [y/N] "
  read -r answer
  if [[ "$answer" =~ ^[Yy]$ ]]; then
    local sp_prefix
    if [ "$MODE" = "trial" ]; then sp_prefix="titan-ai-scanner-trial"; else sp_prefix="titan-ai-remediator"; fi
    local sp_name="${sp_prefix}-$(date +%s)"
    local sp_json
    sp_json=$(az ad sp create-for-rbac --name "$sp_name" \
              --role "Reader" --scopes "/subscriptions/$sub_id" \
              --only-show-errors 2>/dev/null) || die "SP creation failed — contact your Azure admin"
    local app_id secret tenant
    app_id=$(echo "$sp_json" | python3 -c "import sys,json;print(json.load(sys.stdin)['appId'])")
    secret=$(echo "$sp_json" | python3 -c "import sys,json;print(json.load(sys.stdin)['password'])")
    tenant=$(echo "$sp_json" | python3 -c "import sys,json;print(json.load(sys.stdin)['tenant'])")

    # Grant the remaining roles for this mode
    for role in "${required_roles[@]:1}"; do
      az role assignment create --assignee "$app_id" --role "$role" \
        --scope "/subscriptions/$sub_id" --only-show-errors >/dev/null 2>&1 || \
        warn "could not grant '$role' (non-blocking)"
    done
    # Store SP credentials in the .env (mode 0600)
    {
      echo "AZURE_CLIENT_ID=$app_id"
      echo "AZURE_CLIENT_SECRET=$secret"
      echo "AZURE_TENANT_ID=$tenant"
      echo "AZURE_SUBSCRIPTION_ID=$sub_id"
    } >> "$INSTALL_DIR/.env"
    chmod 600 "$INSTALL_DIR/.env"
    ok "azure service principal '$sp_name' created with minimum-privilege read-only roles"
    printf "    ${GRN}Credentials stored in $INSTALL_DIR/.env (mode 0600)${RST}\n"
    printf "    ${GRN}Revoke later: az ad sp delete --id $app_id${RST}\n"
  else
    warn "azure scan will run with current user permissions (may be incomplete)"
  fi
}

# AWS: detect if current identity has SecurityAudit + ReadOnlyAccess policies.
auth_aws() {
  if ! aws sts get-caller-identity >/dev/null 2>&1; then
    warn "aws not configured — run: aws configure OR aws sso login"
    return 1
  fi
  local acct arn user_name
  acct=$(aws sts get-caller-identity --query Account --output text)
  arn=$(aws sts get-caller-identity --query Arn --output text)
  ok "aws authed  account=$acct  arn=$arn"

  # Check for SecurityAudit / ReadOnlyAccess policies
  user_name=$(echo "$arn" | awk -F'/' '{print $NF}')
  local has_audit=false has_readonly=false
  if aws iam list-attached-user-policies --user-name "$user_name" 2>/dev/null | grep -q "SecurityAudit"; then
    has_audit=true
  fi
  if aws iam list-attached-user-policies --user-name "$user_name" 2>/dev/null | grep -q "ReadOnlyAccess"; then
    has_readonly=true
  fi
  if $has_audit && $has_readonly; then
    ok "aws permissions complete (SecurityAudit + ReadOnlyAccess)"
    return 0
  fi

  warn "aws missing policies: $([ "$has_audit" = false ] && echo -n 'SecurityAudit ')$([ "$has_readonly" = false ] && echo -n 'ReadOnlyAccess')"
  printf "    ${YLW}Attach the missing AWS managed policies to this identity? [y/N] ${RST}"
  read -r answer
  if [[ "$answer" =~ ^[Yy]$ ]]; then
    [ "$has_audit"    = false ] && aws iam attach-user-policy --user-name "$user_name" --policy-arn arn:aws:iam::aws:policy/SecurityAudit 2>/dev/null && ok "SecurityAudit attached"
    [ "$has_readonly" = false ] && aws iam attach-user-policy --user-name "$user_name" --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess 2>/dev/null && ok "ReadOnlyAccess attached"
  else
    warn "aws scan may miss resources without those policies"
  fi
}

# GCP: detect if ADC is set and current principal has the minimum viewer roles
auth_gcp() {
  gcloud auth print-access-token >/dev/null 2>&1 || gcloud auth application-default login --quiet
  local project acct
  project=$(gcloud config get-value project 2>/dev/null)
  acct=$(gcloud config get-value account 2>/dev/null)
  ok "gcp authed  project=$project  account=$acct"

  if [ -z "$project" ]; then
    warn "no default GCP project set — run: gcloud config set project PROJECT_ID"
    return 1
  fi

  # Minimum roles for scanning: Security Reviewer + Viewer
  local required_roles=("roles/iam.securityReviewer" "roles/viewer")
  local missing=()
  for role in "${required_roles[@]}"; do
    if ! gcloud projects get-iam-policy "$project" --flatten="bindings[].members" \
         --filter="bindings.members:user:$acct AND bindings.role:$role" \
         --format="value(bindings.role)" 2>/dev/null | grep -q "$role"; then
      missing+=("$role")
    fi
  done

  if [ ${#missing[@]} -eq 0 ]; then
    ok "gcp permissions complete (${required_roles[*]})"
    return 0
  fi

  warn "gcp missing roles: ${missing[*]}"
  printf "    ${YLW}Grant the missing roles to $acct on project $project? [y/N] ${RST}"
  read -r answer
  if [[ "$answer" =~ ^[Yy]$ ]]; then
    for role in "${missing[@]}"; do
      gcloud projects add-iam-policy-binding "$project" \
        --member="user:$acct" --role="$role" --quiet >/dev/null 2>&1 && ok "granted $role" || \
        warn "could not grant $role (admin required)"
    done
  fi
}

{ [ "$CLOUD" = "all" ] || [ "$CLOUD" = "azure" ]; } && have az     && auth_azure || true
{ [ "$CLOUD" = "all" ] || [ "$CLOUD" = "aws"   ]; } && have aws    && auth_aws   || true
{ [ "$CLOUD" = "all" ] || [ "$CLOUD" = "gcp"   ]; } && have gcloud && auth_gcp   || true

# ─── 6. .env config (no secrets in this file) ──────────────────────────────
step "6/8  Configuring environment"
ENV_FILE="$INSTALL_DIR/.env"
{
  echo "# Generated $(date -u +'%Y-%m-%dT%H:%M:%SZ') — DO NOT commit this file"
  echo "TITAN_MODE=$MODE"
  echo "TITAN_TARGET_CLOUDS=$CLOUD"
  echo "TITAN_VERTICAL=$VERTICAL"
  if [ "$AIRLOCK" = "true" ]; then
    echo "TITAN_AIRLOCK_MODE=full"
    echo "OLLAMA_HOST=127.0.0.1:11434"
    echo "TITAN_AIRLOCK_ALLOWLIST=127.0.0.1,localhost"
  else
    echo "TITAN_AIRLOCK_MODE=off"
  fi
  echo "TITAN_READ_ONLY=$([ "$MODE" = "trial" ] && echo true || echo false)"
  echo "TITAN_REQUIRE_HUMAN_APPROVAL=true"
  echo "TITAN_DRY_RUN_DEFAULT=true"
  echo "TITAN_REPORT_FORMATS=html,pdf,docx,json"
} > "$ENV_FILE"
chmod 600 "$ENV_FILE"
ok ".env written (mode=0600)"

# ─── 7. Airlock: local LLM ──────────────────────────────────────────────────
if [ "$AIRLOCK" = "true" ]; then
  step "7/8  Starting local LLM"
  if have ollama; then
    pgrep -x ollama >/dev/null 2>&1 || (ollama serve >/tmp/titan-ollama.log 2>&1 &)
    sleep 2
    ollama pull llama3.1:8b 2>/dev/null || warn "Llama model pull failed — run 'ollama pull llama3.1:8b' manually"
    ok "ollama ready"
  else
    warn "ollama not installed"
  fi
else
  step "7/8  Internet mode — Claude API will be used for AI fixes"
fi

# ─── 8. Run the scan ────────────────────────────────────────────────────────
step "8/8  Running TITAN AI scan"
[ "$MODE" = "trial" ] && printf "    ${YLW}Trial: read-only, no changes to your infrastructure${RST}\n"
[ "$MODE" = "prod"  ] && printf "    ${YLW}Prod: scan + AI proposals, NO auto-execution without human approval${RST}\n"

# Hand control to TITAN CONDUCTOR - the supervisor agent.
# CONDUCTOR reads the license, computes which agents the client's PACKAGE entitles,
# enforces mode (trial = read-only; prod = scan + approval-gated smart-fix),
# and runs ONLY those agents. Nothing outside the package can run.
export TITAN_AGENT="$AGENT"
export TITAN_DOWNLOAD_BASE
python -m agents.conductor

printf "\n${BLD}${GRN}═══ TITAN AI deployment complete ═══${RST}\n"
printf "Reports:  %s/reports/  (HTML + PDF + DOCX + JSON)\n" "$INSTALL_DIR"
printf "Logs:     %s/logs/audit.log  (every AI proposal recorded)\n" "$INSTALL_DIR"
printf "Next:     Open reports/index.html in a browser, review findings, approve fixes\n"
[ "$AIRLOCK" = "true" ] && printf "          ${GRN}Zero outbound internet traffic generated.${RST}\n"
