package main

import (
	"bytes"
	"crypto/sha256"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/jlaffaye/ftp"
)

type FTPConfig struct {
	Host       string `json:"host"`
	Port       int    `json:"port"`
	Username   string `json:"username"`
	Password   string `json:"password"`
	RemoteDir  string `json:"remoteDir"`
	MaxBackups int    `json:"maxBackups"`
}

// getFTPKey 基于连接配置派生加密密钥。
// 注意：此处使用裸 SHA-256 而未加盐与迭代（无 KDF），该简化处理是可接受的，因为：
//  1. 输入包含用户密码等高熵字段；
//  2. 该密钥仅用于已加密配置数据的传输/静态保护；
//  3. 主保护由 ConfigManager 的主密钥提供。
//
// 修改 KDF 会破坏与既有备份的向后兼容，故保持现状。
func (c *ConfigManager) getFTPKey() []byte {
	conf := c.GetFTPConfig()
	if conf == nil {
		return c.key
	}
	hash := sha256.Sum256([]byte(conf.Host + fmt.Sprintf("%d", conf.Port) + conf.Username + conf.Password))
	return hash[:]
}

func (c *ConfigManager) GetFTPConfig() *FTPConfig {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.getFTPConfigLocked()
}

// getFTPConfigLocked 读取 FTP 配置，调用方需持有 c.mu
func (c *ConfigManager) getFTPConfigLocked() *FTPConfig {
	ftpFile := filepath.Join(c.configDir, "ftp.json")
	data, err := os.ReadFile(ftpFile)
	if err != nil {
		return nil
	}
	var conf FTPConfig
	if err := json.Unmarshal(data, &conf); err != nil {
		return nil
	}
	conf.Username = c.decrypt(conf.Username)
	conf.Password = c.decrypt(conf.Password)
	if conf.RemoteDir == "" {
		conf.RemoteDir = "/Lumin/"
	}
	if conf.Port == 0 {
		conf.Port = 21
	}
	return &conf
}

func (c *ConfigManager) SaveFTPConfig(config map[string]string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	existing := c.getFTPConfigLocked()

	username := config["username"]
	password := config["password"]
	if username == "" && existing != nil {
		username = existing.Username
	}
	if password == "" && existing != nil {
		password = existing.Password
	}

	port := 21
	if config["port"] != "" {
		fmt.Sscanf(config["port"], "%d", &port)
	}

	remoteDir := config["remoteDir"]
	if remoteDir == "" {
		remoteDir = "/Lumin/"
	}
	if !strings.HasPrefix(remoteDir, "/") {
		remoteDir = "/" + remoteDir
	}
	if !strings.HasSuffix(remoteDir, "/") {
		remoteDir += "/"
	}

	maxBackups := parseIntOrDefault(config["maxBackups"], 0)

	conf := FTPConfig{
		Host:       config["host"],
		Port:       port,
		RemoteDir:  remoteDir,
		MaxBackups: maxBackups,
	}

	encUser, err := c.encrypt(username)
	if err != nil {
		return fmt.Errorf("encrypt username: %w", err)
	}
	encPass, err := c.encrypt(password)
	if err != nil {
		return fmt.Errorf("encrypt password: %w", err)
	}
	conf.Username = encUser
	conf.Password = encPass
	ftpFile := filepath.Join(c.configDir, "ftp.json")
	data, err := json.MarshalIndent(conf, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal ftp config: %w", err)
	}
	return atomicWriteFile(ftpFile, data, 0600)
}

func (c *ConfigManager) TestFTPConnection(host string, port int, username, password string) error {
	addr := dialAddr(host, port)
	client, err := ftp.Dial(addr, ftp.DialWithTimeout(10*time.Second), ftp.DialWithExplicitTLS(&tls.Config{ServerName: host, MinVersion: tls.VersionTLS12}))
	if err != nil {
		return fmt.Errorf("FTP TLS 连接失败 %s: %w", addr, err)
	}
	defer client.Quit()

	err = client.Login(username, password)
	if err != nil {
		return err
	}
	return nil
}

func (c *ConfigManager) newFTPClient() (*ftp.ServerConn, error) {
	conf := c.GetFTPConfig()
	if conf == nil {
		return nil, fmt.Errorf("FTP not configured")
	}
	addr := dialAddr(conf.Host, conf.Port)
	client, err := ftp.Dial(addr, ftp.DialWithTimeout(10*time.Second), ftp.DialWithExplicitTLS(&tls.Config{ServerName: conf.Host, MinVersion: tls.VersionTLS12}))
	if err != nil {
		return nil, fmt.Errorf("FTP TLS 连接失败 %s: %w", addr, err)
	}
	err = client.Login(conf.Username, conf.Password)
	if err != nil {
		client.Quit()
		return nil, err
	}
	return client, nil
}

func (c *ConfigManager) ensureFTPDir(client *ftp.ServerConn) error {
	conf := c.GetFTPConfig()
	if conf == nil {
		return fmt.Errorf("FTP not configured")
	}

	// Try to change to the remote directory first
	err := client.ChangeDir(conf.RemoteDir)
	if err == nil {
		return nil
	}

	// Directory doesn't exist, create it level by level
	parts := strings.Split(strings.Trim(conf.RemoteDir, "/"), "/")
	current := ""
	for _, part := range parts {
		if part == "" {
			continue
		}
		current += "/" + part
		err := client.ChangeDir(current)
		if err != nil {
			err = client.MakeDir(current)
			if err != nil {
				return fmt.Errorf("failed to create directory %s: %w", current, err)
			}
		}
	}
	// Final change to the target dir
	return client.ChangeDir(conf.RemoteDir)
}

// ─── FTP RemoteStorage 实现 ───────────────────────────────

type ftpStorage struct {
	c          *ConfigManager
	client     *ftp.ServerConn
	remoteDir  string
	key        []byte
	maxBackups int
}

// Close 关闭底层 FTP 连接
func (s *ftpStorage) Close() error {
	if s.client != nil {
		return s.client.Quit()
	}
	return nil
}

func (s *ftpStorage) MaxBackups() int { return s.maxBackups }

func (s *ftpStorage) ListFiles() ([]RemoteFile, error) {
	entries, err := s.client.List(s.remoteDir)
	if err != nil {
		return nil, err
	}
	var result []RemoteFile
	for _, e := range entries {
		result = append(result, RemoteFile{
			Name:    e.Name,
			ModTime: e.Time,
			IsDir:   e.Type == ftp.EntryTypeFolder,
			Size:    int64(e.Size),
		})
	}
	return result, nil
}

func (s *ftpStorage) ReadFile(name string) ([]byte, error) {
	path := strings.TrimRight(s.remoteDir, "/") + "/" + name
	resp, err := s.client.Retr(path)
	if err != nil {
		return nil, err
	}
	defer resp.Close()

	buf := new(bytes.Buffer)
	_, err = buf.ReadFrom(resp)
	return buf.Bytes(), err
}

func (s *ftpStorage) WriteFile(name string, data []byte) error {
	// 目录在 newFTPStorage 时已确保创建，此处无需每次检查
	path := strings.TrimRight(s.remoteDir, "/") + "/" + name
	return s.client.Stor(path, bytes.NewReader(data))
}

func (s *ftpStorage) DeleteFile(name string) error {
	path := strings.TrimRight(s.remoteDir, "/") + "/" + name
	return s.client.Delete(path)
}

func (s *ftpStorage) EncryptKey() []byte { return s.key }

func (c *ConfigManager) newFTPStorage() (RemoteStorage, int, error) {
	conf := c.GetFTPConfig()
	if conf == nil {
		return nil, 0, fmt.Errorf("FTP not configured")
	}
	client, err := c.newFTPClient()
	if err != nil {
		return nil, 0, err
	}
	// 创建存储对象后立即确保远程目录存在，避免每次 WriteFile 都检查
	if err := c.ensureFTPDir(client); err != nil {
		client.Quit()
		return nil, 0, err
	}
	return &ftpStorage{
		c:          c,
		client:     client,
		remoteDir:  conf.RemoteDir,
		key:        c.getFTPKey(),
		maxBackups: conf.MaxBackups,
	}, conf.MaxBackups, nil
}

// BackupToFTP 备份到 FTP
func (c *ConfigManager) BackupToFTP() (map[string]interface{}, error) {
	return c.backupTo(c.newFTPStorage)
}

// ListFTPBackups 列出 FTP 备份
func (c *ConfigManager) ListFTPBackups() ([]map[string]interface{}, error) {
	return c.listBackupsFrom(c.newFTPStorage)
}

// SyncFromFTP 手动合并同步
func (c *ConfigManager) SyncFromFTP() (map[string]interface{}, error) {
	return c.syncFrom(c.newFTPStorage)
}

func (c *ConfigManager) RestoreFromFTPFile(filename string) (map[string]interface{}, error) {
	return c.restoreFrom(c.newFTPStorage, filename)
}
