package middleware

import (
	"net"
	"net/http"

	"code.nonshy.com/nonshy/website/pkg/config"
	"code.nonshy.com/nonshy/website/pkg/controller/index"
	"code.nonshy.com/nonshy/website/pkg/session"
	"code.nonshy.com/nonshy/website/pkg/utility"
	"github.com/oschwald/geoip2-golang"
)

// GeoGate: block access to the site based on the user's location (due to local laws or regulations).
func GeoGate(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !config.GeoGateEnabled {
			handler.ServeHTTP(w, r)
			return
		}

		// Flash errors to admins.
		onError := func(err error) {
			session.FlashError(w, r, "GeoIP: %s", err)
			handler.ServeHTTP(w, r)
		}

		// See where they're coming from.
		db, err := geoip2.Open(config.GeoIPPath)
		if err != nil {
			onError(err)
			return
		}
		defer db.Close()

		// If you are using strings that may be invalid, check that ip is not nil
		addr := utility.IPAddress(r)
		ip := net.ParseIP(addr)
		if ip != nil {
			record, err := db.City(ip)
			if err != nil {
				onError(err)
				return
			}

			// Blocked by US states
			if record.Country.IsoCode == "US" {
				for _, sub := range record.Subdivisions {
					if _, ok := config.BlockUSStates[sub.IsoCode]; ok {
						session.LogoutUser(w, r)
						page := index.StaticTemplate("errors/geo_gate.html")()
						page(w, r)
						return
					}
				}
			}

			// Blocked by country code
			if _, ok := config.BlockCountries[record.Country.IsoCode]; ok {
				session.LogoutUser(w, r)
				page := index.StaticTemplate("errors/geo_gate.html")()
				page(w, r)
				return
			}

			// Debug info
			/*
				fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["pt-BR"])
				if len(record.Subdivisions) > 0 {
					fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"])
				}
				fmt.Printf("Russian country name: %v\n", record.Country.Names["ru"])
				fmt.Printf("ISO country code: %v\n", record.Country.IsoCode)
				fmt.Printf("Time zone: %v\n", record.Location.TimeZone)
				fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
				// Output:
				// Portuguese (BR) city name: Londres
				// English subdivision name: England
				// Russian country name: Великобритания
				// ISO country code: GB
				// Time zone: Europe/London
				// Coordinates: 51.5142, -0.0931
			*/
		}

		handler.ServeHTTP(w, r)
	})
}
