package mmdb

import (
	"context"
	"io"
	"net/http"
	"os"
	"sync"
	"time"

	clashHttp "github.com/Dreamacro/clash/component/http"
	C "github.com/Dreamacro/clash/constant"
	"github.com/Dreamacro/clash/log"

	"github.com/oschwald/maxminddb-golang"
)

type databaseType = uint8

const (
	typeMaxmind databaseType = iota
	typeSing
	typeMetaV0
)

var (
	reader Reader
	once   sync.Once
)

func LoadFromBytes(buffer []byte) {
	once.Do(func() {
		mmdb, err := maxminddb.FromBytes(buffer)
		if err != nil {
			log.Fatalln("Can't load mmdb: %s", err.Error())
		}
		reader = Reader{Reader: mmdb}
		switch mmdb.Metadata.DatabaseType {
		case "sing-geoip":
			reader.databaseType = typeSing
		case "Meta-geoip0":
			reader.databaseType = typeMetaV0
		default:
			reader.databaseType = typeMaxmind
		}
	})
}

func Verify() bool {
	instance, err := maxminddb.Open(C.Path.MMDB())
	if err == nil {
		instance.Close()
	}
	return err == nil
}

func Instance() Reader {
	once.Do(func() {
		mmdbPath := C.Path.MMDB()
		log.Debugln("Load MMDB file: %s", mmdbPath)
		mmdb, err := maxminddb.Open(mmdbPath)
		if err != nil {
			log.Fatalln("Can't load MMDB: %s", err.Error())
		}
		reader = Reader{Reader: mmdb}
		switch mmdb.Metadata.DatabaseType {
		case "sing-geoip":
			reader.databaseType = typeSing
		case "Meta-geoip0":
			reader.databaseType = typeMetaV0
		default:
			reader.databaseType = typeMaxmind
		}
	})

	return reader
}

func DownloadMMDB(path string) (err error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
	defer cancel()
	resp, err := clashHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, resp.Body)

	return err
}
