package cli

import (
	"fmt"
	"time"

	"github.com/urfave/cli/v2"
	"sneak.berlin/go/mfer/internal/checker"
	"sneak.berlin/go/mfer/internal/log"
)

func (mfa *CLIApp) checkManifestOperation(ctx *cli.Context) error {
	log.Debug("checkManifestOperation()")

	// Get manifest path from args, default to index.mf
	manifestPath := "index.mf"
	if ctx.Args().Len() > 0 {
		manifestPath = ctx.Args().Get(0)
	}

	basePath := ctx.String("base")
	showProgress := ctx.Bool("progress")

	log.Debugf("checking manifest %s with base %s", manifestPath, basePath)

	// Create checker
	chk, err := checker.NewChecker(manifestPath, basePath, mfa.Fs)
	if err != nil {
		return fmt.Errorf("failed to load manifest: %w", err)
	}

	log.Debugf("manifest contains %d files, %d bytes", chk.FileCount(), chk.TotalBytes())

	// Set up results channel
	results := make(chan checker.Result, 1)

	// Set up progress channel
	var progress chan checker.CheckStatus
	if showProgress {
		progress = make(chan checker.CheckStatus, 1)
		go func() {
			for status := range progress {
				log.Progressf("Checking: %d/%d files, %d failures",
					status.CheckedFiles,
					status.TotalFiles,
					status.Failures)
			}
			log.ProgressDone()
		}()
	}

	// Process results in a goroutine
	var failures int64
	done := make(chan struct{})
	go func() {
		for result := range results {
			if result.Status != checker.StatusOK {
				failures++
				log.Infof("%s: %s (%s)", result.Status, result.Path, result.Message)
			} else {
				log.Debugf("%s: %s", result.Status, result.Path)
			}
		}
		close(done)
	}()

	// Run check
	err = chk.Check(ctx.Context, results, progress)
	if err != nil {
		return fmt.Errorf("check failed: %w", err)
	}

	// Wait for results processing to complete
	<-done

	// Check for extra files if requested
	if ctx.Bool("no-extra-files") {
		extraResults := make(chan checker.Result, 1)
		extraDone := make(chan struct{})
		go func() {
			for result := range extraResults {
				failures++
				log.Infof("%s: %s (%s)", result.Status, result.Path, result.Message)
			}
			close(extraDone)
		}()

		err = chk.FindExtraFiles(ctx.Context, extraResults)
		if err != nil {
			return fmt.Errorf("failed to check for extra files: %w", err)
		}
		<-extraDone
	}

	if !ctx.Bool("quiet") {
		elapsed := time.Since(mfa.startupTime).Seconds()
		rate := float64(chk.TotalBytes()) / elapsed / 1e6
		if failures == 0 {
			log.Infof("checked %d files (%.1f MB) in %.1fs (%.1f MB/s): all OK", chk.FileCount(), float64(chk.TotalBytes())/1e6, elapsed, rate)
		} else {
			log.Infof("checked %d files (%.1f MB) in %.1fs (%.1f MB/s): %d failed", chk.FileCount(), float64(chk.TotalBytes())/1e6, elapsed, rate, failures)
		}
	}

	if failures > 0 {
		mfa.exitCode = 1
	}

	return nil
}
