package storage

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/sirupsen/logrus"
)

// Storage handles file operations for markdown files
type Storage struct {
	dataDir string
	mu      sync.RWMutex
	log     *logrus.Logger
}

// New creates a new Storage instance
func New(dataDir string, log *logrus.Logger) *Storage {
	return &Storage{
		dataDir: dataDir,
		log:     log,
	}
}

// Init ensures the data directory exists
func (s *Storage) Init() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.log.WithField("dataDir", s.dataDir).Info("Initializing storage")

	if err := os.MkdirAll(s.dataDir, 0755); err != nil {
		s.log.WithError(err).Error("Failed to create data directory")
		return fmt.Errorf("failed to create data directory: %w", err)
	}
	return nil
}

// List returns all markdown files in the data directory
func (s *Storage) List() ([]string, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	s.log.Debug("Listing all files")

	entries, err := os.ReadDir(s.dataDir)
	if err != nil {
		s.log.WithError(err).Error("Failed to read data directory")
		return nil, fmt.Errorf("failed to read data directory: %w", err)
	}

	var files []string
	for _, entry := range entries {
		if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".md") {
			files = append(files, strings.TrimSuffix(entry.Name(), ".md"))
		}
	}

	s.log.WithField("count", len(files)).Debug("Listed files")
	return files, nil
}

// Get reads a markdown file by name
func (s *Storage) Get(name string) (string, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	s.log.WithField("name", name).Debug("Getting file")

	if !isValidFilename(name) {
		s.log.WithField("name", name).Warn("Invalid filename")
		return "", fmt.Errorf("invalid filename")
	}

	path := filepath.Join(s.dataDir, name+".md")
	content, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			s.log.WithField("name", name).Warn("File not found")
			return "", fmt.Errorf("file not found")
		}
		s.log.WithError(err).WithField("name", name).Error("Failed to read file")
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	return string(content), nil
}

// Save writes content to a markdown file
func (s *Storage) Save(name, content string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.log.WithField("name", name).Debug("Saving file")

	if !isValidFilename(name) {
		s.log.WithField("name", name).Warn("Invalid filename")
		return fmt.Errorf("invalid filename")
	}

	path := filepath.Join(s.dataDir, name+".md")
	if err := os.WriteFile(path, []byte(content), 0644); err != nil {
		s.log.WithError(err).WithField("name", name).Error("Failed to write file")
		return fmt.Errorf("failed to write file: %w", err)
	}

	s.log.WithField("name", name).Info("File saved")
	return nil
}

// Delete removes a markdown file
func (s *Storage) Delete(name string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.log.WithField("name", name).Debug("Deleting file")

	if !isValidFilename(name) {
		s.log.WithField("name", name).Warn("Invalid filename")
		return fmt.Errorf("invalid filename")
	}

	path := filepath.Join(s.dataDir, name+".md")
	if err := os.Remove(path); err != nil {
		if os.IsNotExist(err) {
			s.log.WithField("name", name).Warn("File not found for deletion")
			return fmt.Errorf("file not found")
		}
		s.log.WithError(err).WithField("name", name).Error("Failed to delete file")
		return fmt.Errorf("failed to delete file: %w", err)
	}

	s.log.WithField("name", name).Info("File deleted")
	return nil
}

// isValidFilename checks if a filename is safe to use
func isValidFilename(name string) bool {
	if name == "" {
		return false
	}
	// Prevent path traversal
	if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
		return false
	}
	return true
}
