package files

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

// FileInfo represents metadata about a file or directory
type FileInfo struct {
	Name    string `json:"name"`
	Path    string `json:"path"`
	Size    int64  `json:"size"`
	IsDir   bool   `json:"is_dir"`
	ModTime string `json:"mod_time"`
}

// Operations handles file system operations
type Operations struct{}

// NewOperations creates a new file operations handler
func NewOperations() *Operations {
	return &Operations{}
}

// Read reads a file and returns its contents
func (o *Operations) Read(path string) (string, error) {
	log.Printf("Reading file: %s", path)

	// Security: Validate path to prevent directory traversal
	cleanPath, err := o.validatePath(path)
	if err != nil {
		return "", err
	}

	data, err := os.ReadFile(cleanPath)
	if err != nil {
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	log.Printf("Read %d bytes from %s", len(data), path)
	return string(data), nil
}

// Write writes content to a file, downloading from URL if provided
func (o *Operations) Write(path, downloadURL string) error {
	log.Printf("Writing file: %s", path)

	// Security: Validate path
	cleanPath, err := o.validatePath(path)
	if err != nil {
		return err
	}

	// Ensure directory exists
	dir := filepath.Dir(cleanPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("failed to create directory: %w", err)
	}

	// If download URL is provided, fetch content from URL
	if downloadURL != "" {
		return o.downloadAndWrite(cleanPath, downloadURL)
	}

	return fmt.Errorf("no content or download URL provided")
}

// Delete deletes a file or directory
func (o *Operations) Delete(path string) error {
	log.Printf("Deleting: %s", path)

	// Security: Validate path
	cleanPath, err := o.validatePath(path)
	if err != nil {
		return err
	}

	// Check if path exists
	if _, err := os.Stat(cleanPath); os.IsNotExist(err) {
		return fmt.Errorf("path does not exist: %s", path)
	}

	// Remove file or directory
	if err := os.RemoveAll(cleanPath); err != nil {
		return fmt.Errorf("failed to delete: %w", err)
	}

	log.Printf("Deleted: %s", path)
	return nil
}

// List lists files and directories at the given path
func (o *Operations) List(path string) ([]FileInfo, error) {
	log.Printf("Listing directory: %s", path)

	// Security: Validate path
	cleanPath, err := o.validatePath(path)
	if err != nil {
		return nil, err
	}

	// Read directory
	entries, err := os.ReadDir(cleanPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read directory: %w", err)
	}

	var files []FileInfo
	for _, entry := range entries {
		info, err := entry.Info()
		if err != nil {
			log.Printf("Warning: failed to get info for %s: %v", entry.Name(), err)
			continue
		}

		files = append(files, FileInfo{
			Name:    entry.Name(),
			Path:    filepath.Join(path, entry.Name()),
			Size:    info.Size(),
			IsDir:   entry.IsDir(),
			ModTime: info.ModTime().Format(time.RFC3339),
		})
	}

	log.Printf("Listed %d items in %s", len(files), path)
	return files, nil
}

// downloadAndWrite downloads content from URL and writes to file
func (o *Operations) downloadAndWrite(path, url string) error {
	log.Printf("Downloading from %s to %s", url, path)

	// Create HTTP client with timeout
	client := &http.Client{
		Timeout: 5 * time.Minute,
	}

	// Download file
	resp, err := client.Get(url)
	if err != nil {
		return fmt.Errorf("failed to download: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("download failed with status: %d", resp.StatusCode)
	}

	// Create destination file
	file, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("failed to create file: %w", err)
	}
	defer file.Close()

	// Copy content
	written, err := io.Copy(file, resp.Body)
	if err != nil {
		return fmt.Errorf("failed to write file: %w", err)
	}

	log.Printf("Downloaded and wrote %d bytes to %s", written, path)
	return nil
}

// validatePath validates and cleans a file path to prevent directory traversal
func (o *Operations) validatePath(path string) (string, error) {
	// Get absolute path
	absPath, err := filepath.Abs(path)
	if err != nil {
		return "", fmt.Errorf("invalid path: %w", err)
	}

	// Clean the path (removes .. and . elements)
	cleanPath := filepath.Clean(absPath)

	// Basic security check: ensure path doesn't try to escape
	// In production, you might want to restrict to specific directories
	if !filepath.IsAbs(cleanPath) {
		return "", fmt.Errorf("path must be absolute")
	}

	return cleanPath, nil
}

// Exists checks if a file or directory exists
func (o *Operations) Exists(path string) (bool, error) {
	cleanPath, err := o.validatePath(path)
	if err != nil {
		return false, err
	}

	_, err = os.Stat(cleanPath)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
