// Copyright (c) 2026 Alibaba Group and its affiliates

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package transparency

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/dashscope/dashscope-confidential-maas/pkg/tdx"
)

const DSSEPayloadType = "application/vnd.modelstudio.cmaas.release+json"

type LogType string

type ReleaseManifest struct {
	SchemaVersion int                    `json:"schemaVersion"`
	Measurements  map[string]Measurement `json:"measurements"`
}

type Measurement struct {
	Algorithm string `json:"algorithm"`
	Value     string `json:"value"`
}

type ProofEntry struct {
	Type string      `json:"type"`
	Data interface{} `json:"data"`
}

// TranslogService is the interface each transparency log type must implement.
type TranslogService interface {
	FetchProofEntry(ctx context.Context, logIndex int64) (*ProofEntry, error)
	VerifyProofEntry(manifest *ReleaseManifest, entry *ProofEntry) error
	VerifyPayloadHash(manifest *ReleaseManifest, entry *ProofEntry) error
}

// NewTranslogService creates a TranslogService for the given log type.
// config is service-specific (e.g. *RekorV1ServiceConfig for rekor-v1); nil uses built-in defaults.
func NewTranslogService(logType LogType, config any) (TranslogService, error) {
	switch logType {
	case LogTypeRekorV1:
		return newRekorV1Service(config)
	default:
		return nil, fmt.Errorf("transparency: unsupported log type: %s", logType)
	}
}

// VerifyTEEMeasurements cross-validates the ReleaseManifest measurements
// against the provided TEE evidence for the specified TEE type.
// This is independent of any specific transparency log implementation.
func VerifyTEEMeasurements(manifest *ReleaseManifest, evidence json.RawMessage, teeType string) error {
	if len(evidence) == 0 {
		return fmt.Errorf("evidence is required for TEE measurement cross-validation")
	}

	switch teeType {
	case "TDX":
		var tdxEv struct {
			Quote      string `json:"quote"`
			CCEventLog string `json:"cc_eventlog"`
		}
		if err := json.Unmarshal(evidence, &tdxEv); err != nil {
			return fmt.Errorf("parse TDX evidence: %w", err)
		}

		if tdxEv.Quote == "" {
			return fmt.Errorf("quote is required in TDX evidence")
		}
		quoteBytes, err := base64.StdEncoding.DecodeString(tdxEv.Quote)
		if err != nil {
			return fmt.Errorf("decode quote: %w", err)
		}

		if tdxEv.CCEventLog == "" {
			return fmt.Errorf("cc_eventlog is required in TDX evidence")
		}
		eventLogBytes, err := base64.StdEncoding.DecodeString(tdxEv.CCEventLog)
		if err != nil {
			return fmt.Errorf("decode eventlog: %w", err)
		}

		var containerEvents []tdx.AAELContainerEvent
		for name := range manifest.Measurements {
			if strings.HasPrefix(name, "cvm_container_") {
				containerEvents, err = tdx.ExtractAAELContainerEvents(eventLogBytes)
				if err != nil {
					return fmt.Errorf("extract AAEL container events: %w", err)
				}
				break
			}
		}

		for name, m := range manifest.Measurements {
			switch {
			case name == "cvm_firmware":
				got, err := tdx.ExtractMRTD(quoteBytes)
				if err != nil {
					return fmt.Errorf("extract MRTD: %w", err)
				}
				if m.Value != got {
					return fmt.Errorf("cvm_firmware mismatch: manifest=%s quote=%s", m.Value, got)
				}

			case name == "cvm_uki":
				got, err := tdx.ExtractUKIHash(eventLogBytes)
				if err != nil {
					return fmt.Errorf("extract UKI hash: %w", err)
				}
				if m.Value != got {
					return fmt.Errorf("cvm_uki mismatch: manifest=%s eventlog=%s", m.Value, got)
				}

			case name == "cvm_kernel":
				got, err := tdx.ExtractKernelDigest(eventLogBytes)
				if err != nil {
					return fmt.Errorf("extract kernel digest: %w", err)
				}
				if m.Value != got {
					return fmt.Errorf("cvm_kernel mismatch: manifest=%s eventlog=%s", m.Value, got)
				}

			case name == "cvm_kernel_cmdline":
				got, err := tdx.ExtractKernelCmdlineDigest(eventLogBytes)
				if err != nil {
					return fmt.Errorf("extract kernel cmdline digest: %w", err)
				}
				if m.Value != got {
					return fmt.Errorf("cvm_kernel_cmdline mismatch: manifest=%s eventlog=%s", m.Value, got)
				}

			case strings.HasPrefix(name, "cvm_container_"):
				// Match AAEL event to manifest key. Two AAEL forms:
				//   - Legacy / UKI: event has Name == manifest key exactly.
				//   - PAI: event has Reference (OCI image string); we strip
				//     the "cvm_container_" prefix and substring-match it
				//     against the reference.
				short := strings.TrimPrefix(name, "cvm_container_")
				var match *tdx.AAELContainerEvent
				for i, ce := range containerEvents {
					if ce.Name == name || (ce.Reference != "" && strings.Contains(ce.Reference, short)) {
						match = &containerEvents[i]
						break
					}
				}
				if match == nil {
					return fmt.Errorf("container %s declared in manifest but not found in AAEL events", name)
				}
				expected := m.Algorithm + ":" + m.Value
				if match.Digest != expected {
					return fmt.Errorf("container %s mismatch: manifest=%s aael=%s", name, expected, match.Digest)
				}
			default:
				// Unknown measurement type — forward compatible, skip
			}
		}

	default:
		return fmt.Errorf("unsupported TEE type for measurement cross-validation: %s", teeType)
	}
	return nil
}
