// internal/utils/files.go
package utils

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
	"bytes"
)

func init() {
	// Seed the random number generator
	rand.Seed(time.Now().UnixNano())
}

// EnsureDir makes sure the directory exists
func EnsureDir(dir string) error {
	return os.MkdirAll(dir, 0755)
}

// FileExists checks if a file exists and is not a directory
func FileExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	return !info.IsDir()
}

// GetRandomFile returns a random file from the specified directory
// It only includes files with the given extensions (no dot, e.g. "jpg", "png")
// If extensions is empty, it includes all files
func GetRandomFile(dir string, extensions []string) (string, error) {
	files, err := os.ReadDir(dir)
	if err != nil {
		return "", fmt.Errorf("failed to read directory: %w", err)
	}

	// Filter files based on extensions
	var validFiles []string
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		if len(extensions) == 0 {
			validFiles = append(validFiles, file.Name())
			continue
		}

		ext := strings.TrimPrefix(filepath.Ext(file.Name()), ".")
		for _, validExt := range extensions {
			if strings.EqualFold(ext, validExt) {
				validFiles = append(validFiles, file.Name())
				break
			}
		}
	}

	if len(validFiles) == 0 {
		return "", fmt.Errorf("no valid files found in directory")
	}

	// Pick a random file
	randomIndex := rand.Intn(len(validFiles))
	return filepath.Join(dir, validFiles[randomIndex]), nil
}

// MoveFile moves a file from src to dst
func MoveFile(src, dst string) error {
	// Ensure the destination directory exists
	if err := EnsureDir(filepath.Dir(dst)); err != nil {
		return err
	}

	// First try to rename (which works within the same filesystem)
	err := os.Rename(src, dst)
	if err == nil {
		return nil
	}

	// If rename fails (e.g., across filesystems), copy and delete the original
	if err := CopyFile(src, dst); err != nil {
		return err
	}

	return os.Remove(src)
}

// CopyFile copies a file from src to dst
func CopyFile(src, dst string) error {
	// Ensure the destination directory exists
	if err := EnsureDir(filepath.Dir(dst)); err != nil {
		return err
	}

	// Open the source file
	sourceFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sourceFile.Close()

	// Create the destination file
	destFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer destFile.Close()

	// Copy the content
	_, err = io.Copy(destFile, sourceFile)
	if err != nil {
		return err
	}

	// Flush the write buffer to disk
	return destFile.Sync()
}

// CalculateFileHash returns the SHA-256 hash of a file
func CalculateFileHash(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hasher := sha256.New()
	if _, err := io.Copy(hasher, file); err != nil {
		return "", err
	}

	return hex.EncodeToString(hasher.Sum(nil)), nil
}

// GetFileContentType tries to determine the content type of a file
func GetFileContentType(filePath string) (string, error) {
    // Open the file
    file, err := os.Open(filePath)
    if err != nil {
        return "", err
    }
    defer file.Close()

    // Read the first 512 bytes to determine the content type
    buffer := make([]byte, 512)
    bytesRead, err := file.Read(buffer)
    if err != nil && err != io.EOF {
        return "", err
    }
    
    // If we read less than 512 bytes, resize the buffer
    if bytesRead < 512 {
        buffer = buffer[:bytesRead]
    }

    // Reset the file pointer
    _, err = file.Seek(0, 0)
    if err != nil {
        return "", err
    }

    // Detect the content type
    contentType := http.DetectContentType(buffer)
    
    // If the detection gave us a generic type, try to be more specific using the extension
    if contentType == "application/octet-stream" || contentType == "text/plain" {
        ext := strings.ToLower(filepath.Ext(filePath))
        switch ext {
        case ".jpg", ".jpeg":
            return "image/jpeg", nil
        case ".png":
            return "image/png", nil
        case ".gif":
            return "image/gif", nil
        case ".webp":
            return "image/webp", nil
        case ".mp4":
            return "video/mp4", nil
        case ".mov":
            return "video/quicktime", nil
        case ".webm":
            return "video/webm", nil
        case ".mp3":
            return "audio/mpeg", nil
        case ".wav":
            return "audio/wav", nil
        case ".pdf":
            return "application/pdf", nil
        }
    }
    
    // Check for WebP signature, which is sometimes not detected correctly
    if contentType == "application/octet-stream" || contentType == "image/webp" {
        if bytesRead > 12 {
            if bytes.HasPrefix(buffer, []byte("RIFF")) && bytes.Contains(buffer[8:12], []byte("WEBP")) {
                return "image/webp", nil
            }
        }
    }
    
    return contentType, nil
}

// GetSupportedImageExtensions returns a list of supported image extensions
func GetSupportedImageExtensions() []string {
	return []string{"jpg", "jpeg", "png", "gif", "webp", "avif"}
}

// GetSupportedVideoExtensions returns a list of supported video extensions
func GetSupportedVideoExtensions() []string {
	return []string{"mp4", "webm", "mov", "avi"}
}

// GetAllSupportedMediaExtensions returns all supported media extensions
func GetAllSupportedMediaExtensions() []string {
	return append(GetSupportedImageExtensions(), GetSupportedVideoExtensions()...)
}