package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"wucher/internal/config"
	filemanager "wucher/internal/domain/file_manager"
	"wucher/internal/repository/mysql"
	s3repo "wucher/internal/repository/s3"
)

type backfillReport struct {
	Total            int  `json:"total"`
	ActiveFiles      int  `json:"active_files"`
	ProbableOrphans  int  `json:"probable_orphan_files"`
	MissingS3Key     int  `json:"missing_s3_key"`
	InvalidReference int  `json:"invalid_references"`
	Skipped          int  `json:"skipped"`
	DryRun           bool `json:"dry_run"`
}

func main() {
	ctx := context.Background()
	cfg, err := config.LoadFromFile(configFilePath())
	if err != nil {
		fmt.Fprintf(os.Stderr, "load config: %v\n", err)
		os.Exit(1)
	}
	db, err := mysql.Connect(cfg.MySQL)
	if err != nil {
		fmt.Fprintf(os.Stderr, "connect db: %v\n", err)
		os.Exit(1)
	}

	fileRepo := mysql.NewFileManagerFileRepository(db)
	lifecycleRepo, ok := any(fileRepo).(filemanager.FileLifecycleRepository)
	if !ok {
		fmt.Fprintln(os.Stderr, "file repository does not support lifecycle operations")
		os.Exit(1)
	}

	dryRun := true
	if raw := os.Getenv("BACKFILL_DRY_RUN"); raw != "" && (raw == "false" || raw == "0") {
		dryRun = false
	}

	var storage filemanager.ObjectStorage
	if cfg.FileStorage.Bucket != "" && cfg.FileStorage.Region != "" {
		objStorage, s3Err := s3repo.NewFileObjectStorage(ctx, cfg.FileStorage)
		if s3Err == nil {
			storage = objStorage
		}
	}

	rows := make([]filemanager.File, 0)
	if err := db.WithContext(ctx).Model(&filemanager.File{}).Find(&rows).Error; err != nil {
		fmt.Fprintf(os.Stderr, "query files: %v\n", err)
		os.Exit(1)
	}

	report := backfillReport{DryRun: dryRun}
	now := time.Now().UTC()
	for i := range rows {
		row := rows[i]
		report.Total++
		if row.DeletedAt != nil {
			report.Skipped++
			continue
		}

		referenced, refErr := lifecycleRepo.IsFileReferenced(ctx, row.ID)
		if refErr != nil {
			report.InvalidReference++
			report.Skipped++
			continue
		}

		if storage != nil {
			if _, headErr := storage.HeadObject(ctx, row.ObjectKey); headErr != nil {
				report.MissingS3Key++
			}
		}

		if referenced {
			report.ActiveFiles++
			if !dryRun {
				_ = lifecycleRepo.UpdateLifecycle(ctx, filemanager.FileLifecycleTransitionParams{
					ID:              row.ID,
					Status:          filemanager.FileLifecycleStatusActive,
					AttachedAt:      &now,
					ClearOrphanedAt: true,
				})
			}
			continue
		}

		report.ProbableOrphans++
		if !dryRun {
			_ = lifecycleRepo.UpdateLifecycle(ctx, filemanager.FileLifecycleTransitionParams{
				ID:         row.ID,
				Status:     filemanager.FileLifecycleStatusOrphanPending,
				OrphanedAt: &now,
			})
		}
	}

	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	_ = enc.Encode(report)
}

func configFilePath() string {
	if v := os.Getenv("CONFIG_FILE"); v != "" {
		return v
	}
	basePath := os.Getenv("CONFIG_BASE_PATH")
	if basePath == "" {
		basePath = "/tmp"
	}
	filename := os.Getenv("CONFIG_FILENAME")
	if filename == "" {
		filename = "config.generated.json"
	}
	return basePath + "/" + filename
}
