package smb

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"net"
	"os"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/izouxv/logx"
	"golang.org/x/net/webdav"
)

var treeIdX = uint32(0)

func NewAnchor(name, rootpath string) *Anchor {
	return &Anchor{Name: name, RootPath: rootpath, tid: atomic.AddUint32(&treeId, 1)}
}

type Handler struct {
	*webdav.Handler
	// symlink WIP
}

type Anchor struct {
	Name     string
	RootPath string
	tid      uint32
	Handle   *Handler
}
type GetPwdFunc func(name string) (password string, err error)
type GetAnchorFun func(userName string) (anchors []*Anchor, err error)

type Config struct {
	// Port int
	Pwd    GetPwdFunc
	Tree   GetAnchorFun
	Handle func(string) *Handler
}
type ServerI interface {
	Start(PORT int)
}

func NewServer(config *Config) ServerI {
	return &server{config: config, sessions: make(map[uint64]SessionS)}
}

type server struct {
	config   *Config
	sessions map[uint64]SessionS
}

func (s *server) Start(PORT int) {
	// PORT := s.config.Port
	getPwd := s.config.Pwd
	getTree := s.config.Tree
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", PORT))
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		os.Exit(1)
	}
	// Close the listener when the application closes.
	defer l.Close()
	fmt.Printf("Listening on %v port\n", PORT)
	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}

		fmt.Printf("IP: %v", conn.RemoteAddr().String())

		// Handle connections in a new goroutine.
		go s.HandleConnection(conn, getPwd, getTree)

	}
}

func (s *server) HandleConnection(conn net.Conn, getPwd GetPwdFunc, getTree GetAnchorFun) {
	remoteAddr := conn.RemoteAddr()
	defer func() {
		if err := recover(); err != nil {
			b := make([]byte, 4000, 4000)
			n := runtime.Stack(b, false)
			fmt.Printf("%s\n", b[:n])
			fmt.Printf("Error(%v): %s\n\n", remoteAddr, err)
		}
	}()
	defer conn.Close()

	session := NewSessionServer(true, conn, getPwd, getTree)

	if err := session.NegotiateProtocolServer(); err != nil {
		logx.Infof("login failed, %v, err: %v", session.IsAuthenticated, err)
		// time.Sleep(time.Second)
		// conn.Close()
		return
	}

	if !session.IsAuthenticated {
		logx.Infof("login failed, %v", session.IsAuthenticated)
		time.Sleep(time.Second)
		conn.Close()
		return
	}

	logx.Infof("login suc, %v", session.IsAuthenticated)

	rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
	for {
		reqMsg, _, err := session.Recv(rw)
		if err != nil {
			return
		}
		respBuf, cmd, stat := ActionFunc(NewDataCtx(session, conn, s.config.Handle), reqMsg)
		if stat != StatusOk {
			return
		}

		if false {
			fmt.Printf("\n\n\ncmd: %v req:\n%vresp:\n%v", cmd.String(), hex.Dump(reqMsg), hex.Dump(respBuf))
		}

		session.Send(respBuf, rw)
	}
}
