package filesystem

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io"
	"math/big"
	"os"
	"path/filepath"
	"time"
)

// RecycleBinEntry 回收站条目
type RecycleBinEntry struct {
	OriginalPath   string    `json:"original_path"`   // 原始路径
	DeletedPath    string    `json:"deleted_path"`    // 回收站中的路径
	DeletedTime    time.Time `json:"deleted_time"`    // 删除时间
	Size           int64     `json:"size"`            // 文件大小
	IsDirectory    bool      `json:"is_directory"`    // 是否为目录
	OriginalDevice string    `json:"original_device"` // 原始设备（盘符）
}

// RecycleBin 回收站管理器
type RecycleBin struct {
	binPath    string
	metadataFile string
	entries    []RecycleBinEntry
}

// NewRecycleBin 创建回收站管理器
func NewRecycleBin(binPath string) (*RecycleBin, error) {
	// 创建回收站目录
	if err := os.MkdirAll(binPath, 0755); err != nil {
		return nil, fmt.Errorf("创建回收站目录失败: %v", err)
	}

	bin := &RecycleBin{
		binPath:     binPath,
		metadataFile: filepath.Join(binPath, "metadata.json"),
		entries:     make([]RecycleBinEntry, 0),
	}

	// 加载元数据
	if err := bin.loadMetadata(); err != nil {
		// 如果文件不存在，这是正常的，忽略错误
		if !os.IsNotExist(err) {
			return nil, fmt.Errorf("加载回收站元数据失败: %v", err)
		}
	}

	// 启动自动清理协程
	go bin.autoCleanup()

	return bin, nil
}

// MoveToRecycleBin 移动文件到回收站
func (rb *RecycleBin) MoveToRecycleBin(path string) error {
	// 获取文件信息
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("获取文件信息失败: %v", err)
	}

	// 生成唯一的回收站文件名
	timestamp := time.Now().Format("20060102_150405")
	randomSuffix := generateRandomString(6)
	baseName := filepath.Base(path)
	var recycleName string

	if info.IsDir() {
		recycleName = fmt.Sprintf("%s_%s_%s", timestamp, randomSuffix, baseName)
	} else {
		ext := filepath.Ext(baseName)
		nameWithoutExt := baseName[:len(baseName)-len(ext)]
		recycleName = fmt.Sprintf("%s_%s_%s%s", timestamp, randomSuffix, nameWithoutExt, ext)
	}

	recyclePath := filepath.Join(rb.binPath, recycleName)

	// 移动文件到回收站
	if err := os.Rename(path, recyclePath); err != nil {
		// 如果跨设备移动失败，尝试复制后删除
		if err := copyRecursively(path, recyclePath); err != nil {
			return fmt.Errorf("移动到回收站失败: %v", err)
		}
		os.RemoveAll(path)
	}

	// 创建元数据条目
	entry := RecycleBinEntry{
		OriginalPath:   path,
		DeletedPath:    recyclePath,
		DeletedTime:    time.Now(),
		Size:           info.Size(),
		IsDirectory:    info.IsDir(),
		OriginalDevice: getDevice(path),
	}

	// 添加到元数据
	rb.entries = append(rb.entries, entry)

	// 保存元数据
	if err := rb.saveMetadata(); err != nil {
		return fmt.Errorf("保存回收站元数据失败: %v", err)
	}

	return nil
}

// RestoreFromRecycleBin 从回收站恢复文件
func (rb *RecycleBin) RestoreFromRecycleBin(recyclePath string) error {
	// 查找对应的元数据条目
	var entry *RecycleBinEntry
	for i := range rb.entries {
		if rb.entries[i].DeletedPath == recyclePath {
			entry = &rb.entries[i]
			// 从列表中移除
			rb.entries = append(rb.entries[:i], rb.entries[i+1:]...)
			break
		}
	}

	if entry == nil {
		return fmt.Errorf("回收站中未找到该文件")
	}

	// 检查原始路径的父目录是否存在
	parentDir := filepath.Dir(entry.OriginalPath)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return fmt.Errorf("创建父目录失败: %v", err)
	}

	// 检查原始位置是否已有文件
	if _, err := os.Stat(entry.OriginalPath); err == nil {
		return fmt.Errorf("原始位置已存在同名文件，请先删除或重命名")
	}

	// 移回文件
	if err := os.Rename(recyclePath, entry.OriginalPath); err != nil {
		// 如果跨设备移动失败，尝试复制后删除
		if err := copyRecursively(recyclePath, entry.OriginalPath); err != nil {
			return fmt.Errorf("恢复文件失败: %v", err)
		}
		os.RemoveAll(recyclePath)
	}

	// 保存元数据
	if err := rb.saveMetadata(); err != nil {
		return fmt.Errorf("保存回收站元数据失败: %v", err)
	}

	return nil
}

// DeletePermanently 永久删除回收站中的文件
func (rb *RecycleBin) DeletePermanently(recyclePath string) error {
	// 查找元数据条目
	for i, entry := range rb.entries {
		if entry.DeletedPath == recyclePath {
			// 从列表中移除
			rb.entries = append(rb.entries[:i], rb.entries[i+1:]...)
			break
		}
	}

	// 删除文件
	if err := os.RemoveAll(recyclePath); err != nil {
		return fmt.Errorf("永久删除失败: %v", err)
	}

	// 保存元数据
	if err := rb.saveMetadata(); err != nil {
		return fmt.Errorf("保存回收站元数据失败: %v", err)
	}

	return nil
}

// ListEntries 列出回收站中的所有条目
func (rb *RecycleBin) ListEntries() []RecycleBinEntry {
	return rb.entries
}

// Empty 清空回收站
func (rb *RecycleBin) Empty() error {
	// 删除所有文件
	for _, entry := range rb.entries {
		if err := os.RemoveAll(entry.DeletedPath); err != nil {
			return fmt.Errorf("删除文件失败: %s", err)
		}
	}

	// 清空元数据
	rb.entries = make([]RecycleBinEntry, 0)

	// 保存元数据
	if err := rb.saveMetadata(); err != nil {
		return fmt.Errorf("保存回收站元数据失败: %v", err)
	}

	return nil
}

// autoCleanup 自动清理超过30天的文件
func (rb *RecycleBin) autoCleanup() {
	ticker := time.NewTicker(24 * time.Hour) // 每天检查一次
	defer ticker.Stop()

	for range ticker.C {
		rb.cleanupExpiredEntries()
	}
}

// cleanupExpiredEntries 清理过期的条目
func (rb *RecycleBin) cleanupExpiredEntries() {
	now := time.Now()
	expiredEntries := make([]int, 0)

	// 找出所有过期的条目（超过30天）
	for i, entry := range rb.entries {
		if now.Sub(entry.DeletedTime) > 30*24*time.Hour {
			expiredEntries = append(expiredEntries, i)
		}
	}

	// 从后往前删除（避免索引问题）
	for i := len(expiredEntries) - 1; i >= 0; i-- {
		idx := expiredEntries[i]
		entry := rb.entries[idx]

		// 删除文件
		_ = os.RemoveAll(entry.DeletedPath)

		// 从列表中移除
		rb.entries = append(rb.entries[:idx], rb.entries[idx+1:]...)
	}

	// 保存元数据
	if len(expiredEntries) > 0 {
		_ = rb.saveMetadata()
	}
}

// loadMetadata 加载元数据
func (rb *RecycleBin) loadMetadata() error {
	data, err := os.ReadFile(rb.metadataFile)
	if err != nil {
		return err
	}

	return json.Unmarshal(data, &rb.entries)
}

// saveMetadata 保存元数据
func (rb *RecycleBin) saveMetadata() error {
	data, err := json.MarshalIndent(rb.entries, "", "  ")
	if err != nil {
		return err
	}

	return os.WriteFile(rb.metadataFile, data, 0644)
}

// copyRecursively 递归复制文件或目录
func copyRecursively(src, dst string) error {
	info, err := os.Stat(src)
	if err != nil {
		return err
	}

	if info.IsDir() {
		return copyDirectory(src, dst)
	}

	return copyFile(src, dst)
}

// copyDirectory 复制目录
func copyDirectory(src, dst string) error {
	// 创建目标目录
	if err := os.MkdirAll(dst, 0755); err != nil {
		return err
	}

	// 读取源目录
	entries, err := os.ReadDir(src)
	if err != nil {
		return err
	}

	// 复制每个条目
	for _, entry := range entries {
		srcPath := filepath.Join(src, entry.Name())
		dstPath := filepath.Join(dst, entry.Name())

		if entry.IsDir() {
			if err := copyDirectory(srcPath, dstPath); err != nil {
				return err
			}
		} else {
			if err := copyFile(srcPath, dstPath); err != nil {
				return err
			}
		}
	}

	return nil
}

// copyFile 复制文件
func copyFile(src, dst string) error {
	// 打开源文件
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	// 创建目标文件
	dstFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// 复制内容
	if _, err := io.Copy(dstFile, srcFile); err != nil {
		return err
	}

	// 复制文件权限
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	return os.Chmod(dst, srcInfo.Mode())
}

// getDevice 获取文件所在设备（盘符）
func getDevice(path string) string {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return ""
	}

	if len(absPath) >= 2 {
		return absPath[:2] // 返回 "C:" 这样的盘符
	}

	return ""
}

// generateRandomString 生成随机字符串
// 使用加密安全的随机数生成器，保证随机性和性能
func generateRandomString(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
	b := make([]byte, length)

	// 使用 crypto/rand 生成安全的随机数
	for i := range b {
		n, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		if err != nil {
			// 如果加密随机数生成失败，回退到时间戳（极低概率）
			b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
			continue
		}
		b[i] = charset[n.Int64()]
	}

	return string(b)
}

// 全局回收站实例
var globalRecycleBin *RecycleBin

// GetRecycleBin 获取全局回收站实例
func GetRecycleBin() *RecycleBin {
	return globalRecycleBin
}
