package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path"
	"strings"
	"syscall"

	"github.com/gin-gonic/gin"
	"gopkg.in/fsnotify.v1"
)

func (app *App) loadAndWatchIPs(ipfile string) {
	// First load of file if it exists
	app.tryReloadIPList(ipfile)

	// Register SIGHUP
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP)
	go func() {
		for range c {
			log.Println("SIGHUP received, reloading ip list...")
			go app.tryReloadIPList(ipfile)
		}
	}()

	// Watch the configuration file
	if watcher, err := fsnotify.NewWatcher(); err != nil {
		log.Fatal(err)
	} else {
		if err := watcher.Add(path.Dir(ipfile)); err != nil {
			log.Fatal("Unable to watch: ", path.Dir(ipfile), ": ", err)
		}

		go func() {
			defer watcher.Close()
			for {
				select {
				case ev := <-watcher.Events:
					if path.Base(ev.Name) == path.Base(ipfile) && ev.Op&(fsnotify.Write|fsnotify.Create) != 0 {
						log.Println("IPs file changes, reloading them!")
						go app.tryReloadIPList(ipfile)
					}
				case err := <-watcher.Errors:
					log.Println("watcher error:", err)
				}
			}
		}()
	}
}

func (app *App) tryReloadIPList(ipfile string) {
	if _, err := os.Stat(ipfile); !os.IsNotExist(err) {
		if iplist, err := loadIPs(ipfile); err != nil {
			log.Println("ERROR: Unable to read ip file:", err)
		} else {
			log.Printf("Loading %d IPs to authorized list", len(iplist))
			app.ips = iplist
		}
	}
}

func loadIPs(file string) ([]string, error) {
	fd, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer fd.Close()

	var ret []string
	jdec := json.NewDecoder(fd)

	if err := jdec.Decode(&ret); err != nil {
		return ret, err
	}

	return ret, nil
}

func (app *App) Restrict2IPs(c *gin.Context) {
	found := false
	for _, ip := range app.ips {
		if strings.HasPrefix(c.Request.RemoteAddr, ip) {
			found = true
			break
		}
	}

	if found {
		c.Next()
	} else {
		c.AbortWithError(http.StatusForbidden, fmt.Errorf("IP not authorized: %s", c.Request.RemoteAddr))
	}
}
