#!/usr/bin/env bash

set -euo pipefail

PROJECT_ROOT=${PROJECT_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}
WORKFLOW="$PROJECT_ROOT/.gitea/workflows/docker-build.yaml"

failures=0

fail() {
    printf '[АРТЕФАКТЫ] Ошибка: %s\n' "$1" >&2
    failures=$((failures + 1))
}

require_file() {
    local file=$1
    if [[ ! -f "$file" ]]; then
        fail "не найден обязательный файл ${file#"$PROJECT_ROOT"/}"
        return 1
    fi
}

check_dockerfile() {
    local file=$1
    local line

    require_file "$file" || return
    while IFS= read -r line; do
        if [[ ! "$line" =~ ^FROM[[:space:]]+[^[:space:]]+@sha256:[0-9a-f]{64}([[:space:]]+AS[[:space:]]+[A-Za-z0-9._-]+)?$ ]]; then
            fail "базовый образ в ${file#"$PROJECT_ROOT"/} должен содержать полный sha256 digest: $line"
        fi
    done < <(grep -E '^FROM[[:space:]]+' "$file")
}

check_compose_images() {
    local line

    require_file "$PROJECT_ROOT/compose.yaml" || return
    while IFS= read -r line; do
        if [[ ! "$line" =~ image:[[:space:]]+[^[:space:]]+@sha256:[0-9a-f]{64}[[:space:]]*$ ]]; then
            fail "образ Compose должен содержать полный sha256 digest: ${line#*image: }"
        fi
    done < <(grep -E '^[[:space:]]+image:[[:space:]]+' "$PROJECT_ROOT/compose.yaml")
}

check_actions() {
    local action_ref

    require_file "$WORKFLOW" || return
    while IFS= read -r action_ref; do
        if [[ ! "$action_ref" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+@[0-9a-f]{40}$ ]]; then
            fail "Action должен быть закреплён полным commit SHA: $action_ref"
        fi
    done < <(sed -nE 's/^[[:space:]]+uses:[[:space:]]+([^[:space:]#]+).*/\1/p' "$WORKFLOW")
}

require_count() {
    local pattern=$1
    local expected=$2
    local description=$3
    local actual

    actual=$(grep -Ec "$pattern" "$WORKFLOW" || true)
    if (( actual < expected )); then
        fail "$description: ожидается не менее $expected, найдено $actual"
    fi
}

check_workflow_gates() {
    require_file "$WORKFLOW" || return

    require_count '^[[:space:]]+sbom:[[:space:]]+true$' 2 \
        "для обоих образов должна публиковаться SBOM attestation"
    require_count '^[[:space:]]+provenance:[[:space:]]+mode=max$' 2 \
        "для обоих образов должна публиковаться provenance attestation"
    require_count 'image --scanners vuln --exit-code 1 --severity HIGH,CRITICAL' 2 \
        "оба образа должны проходить блокирующее HIGH/CRITICAL-сканирование"
    require_count 'TRIVY_IMAGE: [^[:space:]]+@sha256:[0-9a-f]{64}$' 1 \
        "образ Trivy должен быть закреплён digest"
    require_count 'MAVEN_IMAGE: [^[:space:]]+@sha256:[0-9a-f]{64}$' 1 \
        "образ Maven для CI должен быть закреплён digest"
    require_count 'NODE_IMAGE: [^[:space:]]+@sha256:[0-9a-f]{64}$' 1 \
        "образ Node.js для CI должен быть закреплён digest"

    if ! grep -Eq '^    needs: \[build-and-push-backend, build-and-push-frontend, security-scan\]$' "$WORKFLOW"; then
        fail "deploy обязан зависеть от успешного security-scan"
    fi

    if ! grep -Eq '^      KUBECTL_VERSION: v[0-9]+\.[0-9]+\.[0-9]+$' "$WORKFLOW" \
            || ! grep -Eq '^      KUBECTL_SHA256: [0-9a-f]{64}$' "$WORKFLOW" \
            || ! grep -Fq 'sha256sum --check --strict' "$WORKFLOW"; then
        fail "kubectl должен иметь точную версию и обязательную SHA-256 проверку"
    fi
}

check_downloaded_backend_artifact() {
    local dockerfile="$PROJECT_ROOT/backend/Dockerfile"

    require_file "$dockerfile" || return
    if ! grep -Eq '^ARG OTEL_JAVAAGENT_VERSION=[0-9]+\.[0-9]+\.[0-9]+$' "$dockerfile" \
            || ! grep -Eq '^ARG OTEL_JAVAAGENT_SHA256=[0-9a-f]{64}$' "$dockerfile" \
            || ! grep -Fq 'sha256sum -c -' "$dockerfile"; then
        fail "OpenTelemetry Java Agent должен иметь точную версию и SHA-256 проверку"
    fi
}

check_testcontainer_images() {
    local image_catalog="$PROJECT_ROOT/backend/src/test/java/com/magistr/app/testing/TestContainerImages.java"

    [[ -d "$PROJECT_ROOT/backend/src/test/java" ]] || return 0
    require_file "$image_catalog" || return
    if ! grep -Eq 'postgres:[^"[:space:]]+@sha256:[0-9a-f]{64}' "$image_catalog" \
            || ! grep -Fq 'asCompatibleSubstituteFor("postgres")' "$image_catalog"; then
        fail "образ PostgreSQL Testcontainers должен содержать digest и явную совместимость"
    fi
}

check_optional_kubernetes_images() {
    local k8s_dir=${K8S_DIR:-}
    local line

    [[ -n "$k8s_dir" ]] || return 0
    if [[ ! -d "$k8s_dir" ]]; then
        fail "каталог Kubernetes не найден: $k8s_dir"
        return
    fi

    while IFS= read -r line; do
        if [[ "$line" =~ image:[[:space:]]+([^[:space:]]+) ]]; then
            local image_ref=${BASH_REMATCH[1]}
            if [[ ! "$image_ref" =~ @sha256:[0-9a-f]{64}$ ]]; then
                fail "production-образ Kubernetes должен содержать digest: $image_ref"
            fi
        fi
    done < <(grep -R -h -E '^[[:space:]]+image:[[:space:]]+' "$k8s_dir" --include='*.yaml' --include='*.yml' || true)
}

check_dockerfile "$PROJECT_ROOT/backend/Dockerfile"
check_dockerfile "$PROJECT_ROOT/frontend/Dockerfile"
check_compose_images
check_actions
check_workflow_gates
check_downloaded_backend_artifact
check_testcontainer_images
check_optional_kubernetes_images

if (( failures > 0 )); then
    printf '[АРТЕФАКТЫ] Проверка завершена с ошибками: %d\n' "$failures" >&2
    exit 1
fi

printf '[АРТЕФАКТЫ] Все версии, digest, checksum, SBOM и scan gates проверены\n'
