#!/bin/bash
set -euo pipefail

# POST-CHECK: Security — Tenant Isolation & Authorization (defense-in-depth with MCP)
# S1-S9: Blocks multi-tenant data leakage, RBAC bypass, and authorization errors

FAIL=false

# POST-CHECK S1: All services must filter by TenantId (OWASP A01)
SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
if [ -n "$SERVICE_FILES" ]; then
  for f in $SERVICE_FILES; do
    HAS_TENANT_FILTER=$(grep -c "TenantId" "$f")
    HAS_OPTIONAL_ENTITY=false
    if grep -q "IOptionalTenantEntity\|IScopedTenantEntity" "$f"; then
      HAS_OPTIONAL_ENTITY=true
    fi
    if [ "$HAS_TENANT_FILTER" -eq 0 ] && [ "$HAS_OPTIONAL_ENTITY" = false ]; then
      echo "BLOCKING (OWASP A01): Service missing TenantId filter or optional tenant entity: $f"
      echo "Every service query MUST filter by _currentTenant.TenantId"
      FAIL=true
    fi
    if grep -q "Guid.Empty" "$f"; then
      echo "BLOCKING (OWASP A01): Service uses Guid.Empty instead of _currentTenant.TenantId: $f"
      FAIL=true
    fi
  done
fi

# POST-CHECK S2: Controllers must use [RequirePermission], not just [Authorize] (BLOCKING)
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
if [ -n "$CTRL_FILES" ]; then
  for f in $CTRL_FILES; do
    if grep -q "\[Authorize\]" "$f" && ! grep -q "\[RequirePermission" "$f"; then
      echo "BLOCKING: Controller uses [Authorize] without [RequirePermission]: $f"
      echo "[Authorize] alone provides NO RBAC enforcement — any authenticated user has access"
      echo "Fix: Add [RequirePermission(Permissions.{Module}.{Action})] on each endpoint"
      FAIL=true
    fi
  done
fi

# POST-CHECK S3: Services must inject ICurrentTenantService (tenant isolation)
SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
if [ -n "$SERVICE_FILES" ]; then
  for f in $SERVICE_FILES; do
    if ! grep -qE "ICurrentTenantService|ICurrentUser" "$f"; then
      echo "BLOCKING: Service missing tenant context injection: $f"
      echo "All services MUST inject ICurrentTenantService for tenant isolation"
      FAIL=true
    fi
  done
fi

# POST-CHECK S4: HasQueryFilter must not use Guid.Empty (OWASP A01)
CONFIG_FILES=$(find src/ -path "*/Configurations/*" -name "*Configuration.cs" 2>/dev/null)
if [ -n "$CONFIG_FILES" ]; then
  BAD_FILTER=$(grep -Pn 'HasQueryFilter.*Guid\.Empty' $CONFIG_FILES 2>/dev/null)
  if [ -n "$BAD_FILTER" ]; then
    echo "BLOCKING (OWASP A01): HasQueryFilter uses Guid.Empty — bypasses tenant isolation: $BAD_FILTER"
    echo "The EF global query filter MUST use the injected TenantId, not Guid.Empty"
    FAIL=true
  fi
fi

# POST-CHECK S5: Services must NOT use TenantId!.Value (null-forgiving crash pattern)
SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
if [ -n "$SERVICE_FILES" ]; then
  BAD_PATTERN=$(grep -Pn 'TenantId!\.' $SERVICE_FILES 2>/dev/null)
  if [ -n "$BAD_PATTERN" ]; then
    echo "BLOCKING: TenantId!.Value (null-forgiving) detected — NullReferenceException risk: $BAD_PATTERN"
    echo "Fix: Use guard clause: var tenantId = _currentTenant.TenantId ?? throw new TenantContextRequiredException();"
    FAIL=true
  fi
fi

# POST-CHECK S6: Pages must use lazy loading (no static page imports in routes)
APP_TSX=$(find src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
ROUTE_FILES=$(find src/routes/ -name "*.tsx" -o -name "*.ts" 2>/dev/null)
if [ -n "$APP_TSX" ]; then
  STATIC_IMPORTS=$(grep -Pn "^import .+ from '@/pages/" "$APP_TSX" $ROUTE_FILES 2>/dev/null)
  if [ -n "$STATIC_IMPORTS" ]; then
    echo "BLOCKING: Static page imports in route files — MUST use React.lazy()"
    echo "Static imports load ALL pages upfront, killing initial load performance"
    echo "$STATIC_IMPORTS"
    echo "Fix: const Page = lazy(() => import('@/pages/...').then(m => ({ default: m.Page })))"
    FAIL=true
  fi
fi

# POST-CHECK S7: Controllers must NOT use Guid.Empty for tenantId/userId (OWASP A01)
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
if [ -n "$CTRL_FILES" ]; then
  BAD_GUID=$(grep -Pn 'Guid\.Empty' $CTRL_FILES 2>/dev/null)
  if [ -n "$BAD_GUID" ]; then
    echo "BLOCKING (OWASP A01): Controller uses Guid.Empty — tenant isolation bypassed"
    echo "$BAD_GUID"
    echo "Fix: Use _currentTenant.TenantId from ICurrentTenantService"
    FAIL=true
  fi
fi

# POST-CHECK S8: Write endpoints must NOT use Read permissions (BLOCKING)
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
if [ -n "$CTRL_FILES" ]; then
  for f in $CTRL_FILES; do
    WRITE_WITH_READ=$(grep -Pn '\[(HttpPost|HttpPut|HttpDelete|HttpPatch)' "$f" | while read -r line; do
      LINE_NUM=$(echo "$line" | cut -d: -f1)
      sed -n "$((LINE_NUM-2)),$((LINE_NUM+5))p" "$f" | grep -P 'RequirePermission.*\.Read\b' | head -1
    done)
    if [ -n "$WRITE_WITH_READ" ]; then
      echo "BLOCKING: Write endpoint uses Read permission in $f"
      echo "$WRITE_WITH_READ"
      echo "Fix: POST/PUT/DELETE/PATCH endpoints must use Create/Update/Delete permissions, never Read"
      FAIL=true
    fi
  done
fi

# POST-CHECK S9: FK relationships must enforce tenant isolation (BLOCKING)
CONFIG_FILES=$(find src/ -path "*/Configurations/*" -name "*Configuration.cs" 2>/dev/null)
if [ -n "$CONFIG_FILES" ]; then
  for f in $CONFIG_FILES; do
    ENTITY_NAME=$(grep -oP 'IEntityTypeConfiguration<(\w+)>' "$f" | grep -oP '<\K[^>]+')
    if [ -z "$ENTITY_NAME" ]; then continue; fi

    ENTITY_FILE=$(find src/ -path "*/Domain/*" -name "${ENTITY_NAME}.cs" 2>/dev/null | head -1)
    if [ -z "$ENTITY_FILE" ]; then continue; fi

    IS_TENANT=$(grep -cE 'ITenantEntity|BaseEntity|TenantId' "$ENTITY_FILE")
    if [ "$IS_TENANT" -eq 0 ]; then continue; fi

    FK_TARGETS=$(grep -oP 'HasOne<(\w+)>|HasOne\(e\s*=>\s*e\.(\w+)\)' "$f" | grep -oP '\w+(?=>|\))' | sort -u)
    for TARGET in $FK_TARGETS; do
      TARGET_FILE=$(find src/ -path "*/Domain/*" -name "${TARGET}.cs" 2>/dev/null | head -1)
      if [ -n "$TARGET_FILE" ]; then
        TARGET_IS_TENANT=$(grep -cE 'ITenantEntity|BaseEntity|TenantId' "$TARGET_FILE")
        if [ "$TARGET_IS_TENANT" -gt 0 ]; then
          HAS_QUERY_FILTER=$(grep -c 'HasQueryFilter' "$f")
          if [ "$HAS_QUERY_FILTER" -eq 0 ]; then
            echo "WARNING: $f — FK from tenant entity $ENTITY_NAME to tenant entity $TARGET without HasQueryFilter"
            echo "Cross-tenant data leakage possible if FK is assigned without tenant validation"
            echo "Fix: Add tenant validation in service layer before assigning FK, or use composite FK (TenantId + EntityId)"
          fi
        fi
      fi
    done
  done
fi

if [ "$FAIL" = true ]; then
  exit 1
fi
