package tools

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

// PrependInput contains the parameters for the prepend operation.
type PrependInput struct {
	Folder    string
	Prefix    string
	Recursive bool
	DryRun    bool
}

// PrependResult contains the result of a single file rename operation.
type PrependResult struct {
	Old string `json:"old"`
	New string `json:"new"`
}

// PrependSkipped contains info about a skipped file.
type PrependSkipped struct {
	File   string `json:"file"`
	Reason string `json:"reason"`
}

// PrependError contains info about a failed rename.
type PrependError struct {
	File  string `json:"file"`
	Error string `json:"error"`
}

// PrependOutput contains the complete result of the prepend operation.
type PrependOutput struct {
	Folder    string           `json:"folder"`
	Prefix    string           `json:"prefix"`
	Recursive bool             `json:"recursive"`
	DryRun    bool             `json:"dry_run"`
	Renamed   []PrependResult  `json:"renamed"`
	Skipped   []PrependSkipped `json:"skipped"`
	Errors    []PrependError   `json:"errors"`
}

// datestringRegex matches filenames starting with YYYYMMDD_HHMMSS.
var datestringRegex = regexp.MustCompile(`^\d{8}_\d{6}\.`)

// Prepend renames files in a folder by prepending a prefix.
// WAV files (.wav, .WAV) and their .data files are only renamed if they start with a datestring.
// log.txt is always renamed if present.
func Prepend(input PrependInput) (*PrependOutput, error) {
	output := &PrependOutput{
		Folder:    input.Folder,
		Prefix:    input.Prefix,
		Recursive: input.Recursive,
		DryRun:    input.DryRun,
		Renamed:   []PrependResult{},
		Skipped:   []PrependSkipped{},
		Errors:    []PrependError{},
	}

	// Collect folders to process
	folders := []string{input.Folder}
	if input.Recursive {
		entries, err := os.ReadDir(input.Folder)
		if err != nil {
			return nil, fmt.Errorf("failed to read folder: %w", err)
		}
		for _, entry := range entries {
			if entry.IsDir() {
				folders = append(folders, filepath.Join(input.Folder, entry.Name()))
			}
		}
	}

	// Process each folder
	for _, folder := range folders {
		entries, err := os.ReadDir(folder)
		if err != nil {
			return nil, fmt.Errorf("failed to read folder %s: %w", folder, err)
		}

		for _, entry := range entries {
			if entry.IsDir() {
				continue
			}

			filename := entry.Name()
			oldPath := filepath.Join(folder, filename)
			shouldRename, skipReason := shouldPrependFile(filename, input.Prefix)

			if !shouldRename {
				if skipReason != "" {
					output.Skipped = append(output.Skipped, PrependSkipped{
						File:   oldPath,
						Reason: skipReason,
					})
				}
				continue
			}

			newFilename := input.Prefix + "_" + filename
			newPath := filepath.Join(folder, newFilename)

			if input.DryRun {
				output.Renamed = append(output.Renamed, PrependResult{
					Old: oldPath,
					New: newPath,
				})
				continue
			}

			// Perform the rename
			if err := os.Rename(oldPath, newPath); err != nil {
				output.Errors = append(output.Errors, PrependError{
					File:  oldPath,
					Error: err.Error(),
				})
				continue
			}

			output.Renamed = append(output.Renamed, PrependResult{
				Old: oldPath,
				New: newPath,
			})
		}
	}

	return output, nil
}

// shouldPrependFile determines if a file should be prepended.
// Returns (shouldRename, skipReason). If shouldRename is false and skipReason is empty,
// the file is not a target type (silently ignored).
func shouldPrependFile(filename, prefix string) (bool, string) {
	lowerName := strings.ToLower(filename)

	// Check if already prefixed (applies to all target files)
	if strings.HasPrefix(filename, prefix+"_") {
		// Only report as "already prefixed" if it's a target file type
		if filename == prefix+"_log.txt" || isWavOrData(lowerName) {
			return false, "already prefixed"
		}
		return false, ""
	}

	// Check for log.txt (exact match, case-sensitive as per spec)
	if filename == "log.txt" {
		return true, ""
	}

	// Check for WAV files and their .data files
	if !isWavOrData(lowerName) {
		return false, "" // Not a target file type, silently ignore
	}

	// Check for datestring prefix (YYYYMMDD_HHMMSS.)
	if !datestringRegex.MatchString(filename) {
		return false, "no datestring prefix"
	}

	return true, ""
}

// isWavOrData checks if the lowercase filename is a .wav or .wav.data file
func isWavOrData(lowerName string) bool {
	return strings.HasSuffix(lowerName, ".wav") || strings.HasSuffix(lowerName, ".wav.data")
}