// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 table

import (
	
	
	
	
	

	
	
	
)

type Operation string

const (
	OpAppend    Operation = "append"
	OpReplace   Operation = "replace"
	OpOverwrite Operation = "overwrite"
	OpDelete    Operation = "delete"
)

var (
	ErrInvalidOperation = errors.New("invalid operation value")
	ErrMissingOperation = errors.New("missing operation key")
)

// ValidOperation ensures that a given string is one of the valid operation
// types: append,replace,overwrite,delete
func ( string) (Operation, error) {
	switch  {
	case "append", "replace", "overwrite", "delete":
		return Operation(), nil
	}
	return "", fmt.Errorf("%w: found '%s'", ErrInvalidOperation, )
}

const operationKey = "operation"

// Summary stores the summary information for a snapshot indicating
// the operation that created the snapshot, and various properties
// which might exist in the summary.
type Summary struct {
	Operation  Operation
	Properties map[string]string
}

func ( *Summary) () string {
	 := string(.Operation)
	if .Properties != nil {
		,  := json.Marshal(.Properties)
		 += ", " + string()
	}
	return 
}

func ( *Summary) ( *Summary) bool {
	if  ==  {
		return true
	}

	if  != nil &&  == nil {
		return false
	}

	if .Operation != .Operation {
		return false
	}

	if len(.Properties) == 0 && len(.Properties) == 0 {
		return true
	}

	return maps.Equal(.Properties, .Properties)
}

func ( *Summary) ( []byte) ( error) {
	 := map[string]string{}
	if  = json.Unmarshal(, &);  != nil {
		return
	}

	,  := [operationKey]
	if ! {
		return ErrMissingOperation
	}

	if .Operation,  = ValidOperation();  != nil {
		return
	}

	delete(, operationKey)
	.Properties = 
	return nil
}

func ( *Summary) () ([]byte, error) {
	 := maps.Clone(.Properties)
	if .Operation != "" {
		if  == nil {
			 = make(map[string]string)
		}
		[operationKey] = string(.Operation)
	}

	return json.Marshal()
}

type Snapshot struct {
	SnapshotID       int64    `json:"snapshot-id"`
	ParentSnapshotID *int64   `json:"parent-snapshot-id,omitempty"`
	SequenceNumber   int64    `json:"sequence-number"`
	TimestampMs      int64    `json:"timestamp-ms"`
	ManifestList     string   `json:"manifest-list,omitempty"`
	Summary          *Summary `json:"summary,omitempty"`
	SchemaID         *int     `json:"schema-id,omitempty"`
}

func ( Snapshot) () string {
	var (
		, ,  string
	)

	if .Summary != nil {
		 = .Summary.String() + ": "
	}
	if .ParentSnapshotID != nil {
		 = ", parent_id=" + strconv.FormatInt(*.ParentSnapshotID, 10)
	}
	if .SchemaID != nil {
		 = ", schema_id=" + strconv.Itoa(*.SchemaID)
	}
	return fmt.Sprintf("%sid=%d%s%s, sequence_number=%d, timestamp_ms=%d, manifest_list=%s",
		, .SnapshotID, , , .SequenceNumber, .TimestampMs, .ManifestList)
}

func ( Snapshot) ( Snapshot) bool {
	switch {
	case .ParentSnapshotID == nil && .ParentSnapshotID != nil:
		fallthrough
	case .ParentSnapshotID != nil && .ParentSnapshotID == nil:
		fallthrough
	case .SchemaID == nil && .SchemaID != nil:
		fallthrough
	case .SchemaID != nil && .SchemaID == nil:
		return false
	}

	return .SnapshotID == .SnapshotID &&
		((.ParentSnapshotID == .ParentSnapshotID) || (*.ParentSnapshotID == *.ParentSnapshotID)) &&
		((.SchemaID == .SchemaID) || (*.SchemaID == *.SchemaID)) &&
		.SequenceNumber == .SequenceNumber &&
		.TimestampMs == .TimestampMs &&
		.ManifestList == .ManifestList &&
		.Summary.Equals(.Summary)
}

func ( Snapshot) ( objstore.Bucket) ([]iceberg.ManifestFile, error) {
	if .ManifestList != "" {
		,  := .Get(context.TODO(), .ManifestList)
		if  != nil {
			return nil, fmt.Errorf("could not open manifest file: %w", )
		}
		defer .Close()
		return iceberg.ReadManifestList()
	}

	return nil, nil
}

type MetadataLogEntry struct {
	MetadataFile string `json:"metadata-file"`
	TimestampMs  int64  `json:"timestamp-ms"`
}

type SnapshotLogEntry struct {
	SnapshotID  int64 `json:"snapshot-id"`
	TimestampMs int64 `json:"timestamp-ms"`
}