package main

import (
	"canguidev/shelfy/internal/db"
	"canguidev/shelfy/internal/routes"
	"canguidev/shelfy/internal/utils"
	"crypto/tls"
	"errors"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	ftpserver "github.com/fclairamb/ftpserverlib"
	"github.com/gin-gonic/gin"
	"github.com/spf13/afero"
)

// ---------- FTP SERVERLIB DRIVERS ----------

type mainDriver struct{}

type clientDriver struct {
	fs afero.Fs
}

func (d *mainDriver) GetSettings() (*ftpserver.Settings, error) {
	return &ftpserver.Settings{
		ListenAddr: ":2121",
		PublicHost: "163.172.68.103",
		PassiveTransferPortRange: &ftpserver.PortRange{
			Start: 30000,
			End:   30100,
		},
		Banner:           "Bienvenue sur le FTP Go!",
		IdleTimeout:      0,
		ConnectionTimeout: 0,

	}, nil
}

func (d *mainDriver) ClientConnected(cc ftpserver.ClientContext) (string, error) {
	log.Printf("[FTP] Nouvelle connexion depuis %s", cc.RemoteAddr())
	return "Bienvenue FTP !", nil
}
func (d *mainDriver) ClientDisconnected(cc ftpserver.ClientContext) {
	log.Printf("[FTP] Déconnexion client %s", cc.RemoteAddr())
}
func (d *mainDriver) GetTLSConfig() (*tls.Config, error) {
	return nil, nil // Pas de TLS, à gérer si tu veux FTPS
}
func (d *mainDriver) AuthUser(cc ftpserver.ClientContext, user, pass string) (ftpserver.ClientDriver, error) {
	log.Printf("[FTP] Tentative login user='%s' pass='%s'", user, pass)
	remoteAddr := cc.RemoteAddr().String()
	host, _, _ := net.SplitHostPort(remoteAddr)

	if user == "cangui2089" && pass == "GHT30k7!" {
		uploadPath, _ := filepath.Abs("upload")
		fi, err := os.Stat(uploadPath)
		if err != nil || !fi.IsDir() {
			log.Printf("[FTP] Le dossier upload/ est manquant ou non valide : %v", err)
			return nil, errors.New("le dossier upload/ doit exister")
		}
		log.Printf("[FTP] Connexion OK, exposé: %s", uploadPath)
		return &clientDriver{
			fs: afero.NewBasePathFs(afero.NewOsFs(), uploadPath),
		}, nil
	}
	// 2. Autoriser anonymous depuis 192.168.1.123 uniquement
	if user == "anonymous" && host == "82.65.73.115" {
		base := filepath.Clean("upload")
		fs := afero.NewBasePathFs(afero.NewOsFs(), base)
		log.Printf("[FTP] Login ANONYMOUS autorisé pour %s", host)
		return  &clientDriver{fs: fs}, nil
	}

	// 3. Sinon refuse tout anonyme ou mauvais login
	if user == "" || user == "anonymous" {
		log.Printf("[FTP] Login anonymous refusé pour %s", host)
		return nil, errors.New("anonymous login not allowed")
	}

	return nil, errors.New("identifiants invalides")
}

// ---------- clientDriver = wrapper sur afero.Fs ----------

func (c *clientDriver) Name() string                                       { return "aferofs" }
func (c *clientDriver) Create(name string) (afero.File, error)             { return c.fs.Create(name) }
func (c *clientDriver) Mkdir(name string, perm os.FileMode) error          { return c.fs.Mkdir(name, perm) }
func (c *clientDriver) MkdirAll(path string, perm os.FileMode) error       { return c.fs.MkdirAll(path, perm) }
func (c *clientDriver) Open(name string) (afero.File, error)               { return c.fs.Open(name) }
func (c *clientDriver) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
	    log.Printf("[FTP][DEBUG] Open: %s", name)
	return c.fs.OpenFile(name, flag, perm)
}
func (c *clientDriver) Remove(name string) error           { return c.fs.Remove(name) }
func (c *clientDriver) RemoveAll(path string) error        { return c.fs.RemoveAll(path) }
func (c *clientDriver) Rename(old, new string) error       { return c.fs.Rename(old, new) }
func (c *clientDriver) Stat(name string) (os.FileInfo, error) {
	return c.fs.Stat(name)
}
func (c *clientDriver) LstatIfPossible(name string) (os.FileInfo, bool, error) {
	fi, err := c.fs.Stat(name)
	return fi, false, err
}
func (c *clientDriver) Chmod(name string, mode os.FileMode) error {
	fullPath := filepath.Join("upload", name)
	return os.Chmod(fullPath, mode)
}
func (c *clientDriver) Chown(name string, uid, gid int) error {
	fullPath := filepath.Join("upload", name)
	return os.Chown(fullPath, uid, gid)
}
func (c *clientDriver) Chtimes(name string, atime, mtime time.Time) error {
	fullPath := filepath.Join("upload", name)
	return os.Chtimes(fullPath, atime, mtime)
}


// ---------- MAIN ----------

func main() {
	// Serveur FTP dans une goroutine
	go func() {
		ftp := ftpserver.NewFtpServer(&mainDriver{})
		log.Println("[FTP] Serveur FTP en écoute sur ftp://cangui2089:GHT30k7!@localhost:2121 (upload/)")
		if err := ftp.ListenAndServe(); err != nil {
			log.Fatal("[FTP] Erreur:", err)
		}
	}()

	// Serveur HTTP Gin (classique)
	bd := db.InitDB()
	app := gin.Default()

	api := app.Group("/api/v1")
	routes.AddRoutes(api, bd)
	utils.CreateDefaultFolder(bd)

	app.Static("/static", "./web")
	app.NoRoute(func(c *gin.Context) {
		if strings.HasPrefix(c.Request.URL.Path, "/api/") {
			c.JSON(404, gin.H{"error": "Not found"})
			return
		}
		c.File("./web/index.html")
	})

	log.Println("[HTTP] Serveur Gin sur http://localhost:8080")
	app.Run(":8080")
}
