// Command wallet-outbox-archive-restore validates one downloaded COS batch and,
// only after _SUCCESS and every checksum pass, writes it to isolation tables.
package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"hyapp/services/wallet-service/internal/outboxarchiverestore"
)

const defaultDSNEnv = "WALLET_OUTBOX_ARCHIVE_RESTORE_DSN"

func main() {
	dataPath := flag.String("data", "", "local data.ndjson.gz downloaded from the private archive bucket")
	manifestPath := flag.String("manifest", "", "local manifest.json from the same batch directory")
	successPath := flag.String("success", "", "local _SUCCESS from the same batch directory")
	restore := flag.Bool("restore", false, "persist the verified batch into fixed isolation tables")
	dsnEnv := flag.String("dsn-env", defaultDSNEnv, "environment variable containing the restore-target MySQL DSN")
	expectedDatabase := flag.String("expected-database", "", "exact restore-target database returned by SELECT DATABASE()")
	rehearsalID := flag.String("rehearsal-id", "", "stable 1..64 character restore rehearsal key")
	confirmation := flag.String("confirm", "", "restore confirmation RESTORE:<rehearsal-id>:<batch-id>")
	timeout := flag.Duration("timeout", 10*time.Minute, "whole verification/restore timeout, at most 30m")
	flag.Parse()

	if strings.TrimSpace(*dataPath) == "" || strings.TrimSpace(*manifestPath) == "" || strings.TrimSpace(*successPath) == "" {
		fatal(errors.New("--data, --manifest, and --success are required"))
	}
	if *timeout <= 0 || *timeout > 30*time.Minute {
		fatal(errors.New("--timeout must be within (0,30m]"))
	}
	ctx, cancel := context.WithTimeout(context.Background(), *timeout)
	defer cancel()

	verification, err := outboxarchiverestore.VerifyLocalFiles(*dataPath, *manifestPath, *successPath, outboxarchiverestore.DefaultLimits())
	if err != nil {
		fatal(err)
	}
	if !*restore {
		printJSON(map[string]any{
			"state": "FILES_VERIFIED", "batch_id": verification.Manifest.BatchID,
			"app_code": verification.Manifest.AppCode, "row_count": verification.Manifest.RowCount,
			"data_object_key": verification.Manifest.DataObjectKey,
			"first_cursor":    verification.Manifest.FirstCursor, "last_cursor": verification.Manifest.LastCursor,
			"ndjson_sha256": verification.Manifest.NDJSONSHA256, "gzip_sha256": verification.Manifest.GzipSHA256,
			"data_crc64":      verification.Manifest.DataCRC64,
			"manifest_sha256": verification.ManifestSHA256, "manifest_crc64": verification.ManifestCRC64,
			"success_sha256": verification.SuccessSHA256, "success_crc64": verification.SuccessCRC64,
			"available_delta_total": verification.Manifest.AvailableDeltaTotal,
			"frozen_delta_total":    verification.Manifest.FrozenDeltaTotal, "no_database_writes": true,
		})
		return
	}

	rehearsal := strings.TrimSpace(*rehearsalID)
	expectedConfirmation := "RESTORE:" + rehearsal + ":" + verification.Manifest.BatchID
	if rehearsal == "" || strings.TrimSpace(*confirmation) != expectedConfirmation {
		fatal(fmt.Errorf("--restore requires --rehearsal-id and --confirm=%s", expectedConfirmation))
	}
	envName := strings.TrimSpace(*dsnEnv)
	if envName == "" {
		fatal(errors.New("--dsn-env cannot be empty"))
	}
	dsn := strings.TrimSpace(os.Getenv(envName))
	if dsn == "" {
		fatal(fmt.Errorf("restore DSN environment variable %s is empty", envName))
	}
	result, err := outboxarchiverestore.Restore(ctx, dsn, *expectedDatabase, rehearsal, verification)
	if err != nil {
		fatal(err)
	}
	printJSON(struct {
		outboxarchiverestore.RestoreResult
		DSNEnv string `json:"dsn_env"`
	}{RestoreResult: result, DSNEnv: envName})
}

func printJSON(value any) {
	body, err := json.Marshal(value)
	if err != nil {
		fatal(err)
	}
	fmt.Println(string(body))
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}
