package hub

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"net/netip"
	"strings"
	"vppn/hub/api"
	"vppn/m"

	"git.crumpington.com/lib/go/webutil"
	"golang.org/x/crypto/bcrypt"
)

func (a *App) _root(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	if s.SignedIn {
		return a.redirect(w, r, "/admin/network/list/")
	} else {
		return a.redirect(w, r, "/sign-in/")
	}
}

func (a *App) _signin(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	return a.render("/sign-in.html", w, struct{ Session *api.Session }{s})
}

func (a *App) _signinSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	var pwd string
	err := webutil.NewFormScanner(r.Form).
		Scan("Password", &pwd).
		Error()
	if err != nil {
		return err
	}

	if err := a.api.Session_SignIn(s, pwd); err != nil {
		return err
	}

	return a.redirect(w, r, "/")
}

func (a *App) _adminSignOut(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	return a.render("/admin-sign-out.html", w, struct{ Session *api.Session }{s})
}

func (a *App) _adminSignOutSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	if err := a.api.Session_Delete(s.SessionID); err != nil {
		log.Printf("Failed to delete session cookie %s: %v", s.SessionID, err)
	}
	a.deleteCookie(w, SESSION_ID_COOKIE_NAME)
	return a.redirect(w, r, "/")
}

func (a *App) _adminNetworkList(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	l, err := a.api.Network_List()
	if err != nil {
		return err
	}
	return a.render("/admin-network-list.html", w, struct {
		Session  *api.Session
		Networks []*api.Network
	}{s, l})
}

func (a *App) _adminNetworkCreate(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	return a.render("/admin-network-create.html", w, struct{ Session *api.Session }{s})
}

func (a *App) _adminNetworkCreateSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n := &api.Network{}
	var netStr string

	err := webutil.NewFormScanner(r.Form).
		Scan("Name", &n.Name).
		Scan("Network", &netStr).
		Error()
	if err != nil {
		return err
	}

	n.Network, err = stringToIP(netStr)
	if err != nil {
		return err
	}

	if err := a.api.Network_Create(n); err != nil {
		return err
	}

	return a.redirect(w, r, "/admin/network/view/?NetworkID=%d", n.NetworkID)
}

func (a *App) _adminNetworkView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, peers, err := a.formGetNetworkPeers(r.Form)
	if err != nil {
		return err
	}

	return a.render("/network/network-view.html", w, struct {
		Session *api.Session
		Network *api.Network
		Peers   []*api.Peer
	}{s, n, peers})
}

func (a *App) _adminNetworkDelete(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, peers, err := a.formGetNetworkPeers(r.Form)
	if err != nil {
		return err
	}

	return a.render("/network/network-delete.html", w, struct {
		Session *api.Session
		Network *api.Network
		Peers   []*api.Peer
	}{s, n, peers})
}

func (a *App) _adminNetworkDeleteSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, err := a.formGetNetwork(r.Form)
	if err != nil {
		return err
	}

	if err = a.api.Network_Delete(n); err != nil {
		return err
	}
	return a.redirect(w, r, "/admin/network/list/")
}

func (a *App) _adminPeerCreate(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, err := a.formGetNetwork(r.Form)
	if err != nil {
		return err
	}

	return a.render("/network/peer-create.html", w, struct {
		Session *api.Session
		Network *api.Network
	}{s, n})
}

func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	var ipStr string

	p := &api.Peer{}
	err := webutil.NewFormScanner(r.Form).
		Scan("NetworkID", &p.NetworkID).
		Scan("IP", &p.PeerIP).
		Scan("Name", &p.Name).
		Scan("PublicIP", &ipStr).
		Scan("Port", &p.Port).
		Scan("Relay", &p.Relay).
		Error()
	if err != nil {
		return err
	}

	if p.PublicIP, err = stringToIP(ipStr); err != nil {
		return err
	}

	if err := a.api.Peer_CreateNew(p); err != nil {
		return err
	}
	return a.redirect(w, r, "/admin/peer/view/?NetworkID=%d&PeerIP=%d", p.NetworkID, p.PeerIP)
}

func (a *App) _adminPeerView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	net, peer, err := a.formGetPeer(r.Form)
	if err != nil {
		return err
	}

	return a.render("/network/peer-view.html", w, struct {
		Session *api.Session
		Network *api.Network
		Peer    *api.Peer
	}{s, net, peer})
}

func (a *App) _adminPeerEdit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	net, peer, err := a.formGetPeer(r.Form)
	if err != nil {
		return err
	}

	return a.render("/network/peer-edit.html", w, struct {
		Session *api.Session
		Network *api.Network
		Peer    *api.Peer
	}{s, net, peer})
}

func (a *App) _adminPeerEditSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	_, peer, err := a.formGetPeer(r.Form)
	if err != nil {
		return err
	}

	var ipStr string

	err = webutil.NewFormScanner(r.Form).
		Scan("Name", &peer.Name).
		Scan("PublicIP", &ipStr).
		Scan("Port", &peer.Port).
		Scan("Relay", &peer.Relay).
		Error()
	if err != nil {
		return err
	}

	if peer.PublicIP, err = stringToIP(ipStr); err != nil {
		return err
	}

	if err = a.api.Peer_Update(peer); err != nil {
		return err
	}

	return a.redirect(w, r, "/admin/peer/view/?NetworkID=%d&PeerIP=%d", peer.NetworkID, peer.PeerIP)
}

func (a *App) _adminPeerDelete(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, peer, err := a.formGetPeer(r.Form)
	if err != nil {
		return err
	}

	return a.render("/network/peer-delete.html", w, struct {
		Session *api.Session
		Network *api.Network
		Peer    *api.Peer
	}{s, n, peer})
}

func (a *App) _adminPeerDeleteSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, peer, err := a.formGetPeer(r.Form)
	if err != nil {
		return err
	}
	if err := a.api.Peer_Delete(n.NetworkID, peer.PeerIP); err != nil {
		return err
	}
	return a.redirect(w, r, "/admin/network/view/?NetworkID=%d", n.NetworkID)
}

func (a *App) _adminNetworkHosts(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	n, peers, err := a.formGetNetworkPeers(r.Form)
	if err != nil {
		return err
	}

	b := strings.Builder{}

	for _, peer := range peers {
		ip := n.Network
		ip[3] = peer.PeerIP
		b.WriteString(netip.AddrFrom4([4]byte(ip)).String())
		b.WriteString(" ")
		b.WriteString(peer.Name)
		b.WriteString("\n")
	}

	w.Write([]byte(b.String()))
	return nil
}

func (a *App) _adminPasswordEdit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	return a.render("/admin-password-edit.html", w, struct{ Session *api.Session }{s})
}

func (a *App) _adminPasswordSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
	var (
		conf    = a.api.Config_Get()
		curPwd  string
		newPwd  string
		newPwd2 string
	)

	err := webutil.NewFormScanner(r.Form).
		Scan("CurrentPassword", &curPwd).
		Scan("NewPassword", &newPwd).
		Scan("NewPassword2", &newPwd2).
		Error()
	if err != nil {
		return err
	}

	if len(newPwd) < 8 {
		return errors.New("password is too short")
	}

	if newPwd != newPwd2 {
		return errors.New("passwords don't match")
	}

	err = bcrypt.CompareHashAndPassword(conf.Password, []byte(curPwd))
	if err != nil {
		return err
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(newPwd), bcrypt.DefaultCost)
	if err != nil {
		return err
	}

	conf.Password = hash

	if err := a.api.Config_Update(conf); err != nil {
		return err
	}

	return a.redirect(w, r, "/admin/config/")
}

func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
	args := m.PeerInitArgs{}
	if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
		return err
	}

	net, err := a.api.Network_Get(peer.NetworkID)
	if err != nil {
		return err
	}

	if err := a.api.Peer_Init(peer, args); err != nil {
		return err
	}

	resp := m.PeerInitResp{
		PeerIP:  peer.PeerIP,
		Network: net.Network,
	}

	resp.NetworkState.Peers, err = a.peersArray(net.NetworkID)
	if err != nil {
		return err
	}

	return a.sendJSON(w, resp)
}

func (a *App) _peerFetchState(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {

	peers, err := a.peersArray(peer.NetworkID)
	if err != nil {
		return err
	}
	return a.sendJSON(w, m.NetworkState{Peers: peers})
}

func (a *App) peersArray(networkID int64) (peers [256]*m.Peer, err error) {
	l, err := a.api.Peer_List(networkID)
	if err != nil {
		return peers, err
	}

	for _, p := range l {
		if len(p.PubKey) != 0 {
			peers[p.PeerIP] = &m.Peer{
				PeerIP:     p.PeerIP,
				Version:    p.Version,
				Name:       p.Name,
				PublicIP:   p.PublicIP,
				Port:       p.Port,
				Relay:      p.Relay,
				PubKey:     p.PubKey,
				PubSignKey: p.PubSignKey,
			}
		}
	}

	return
}
