package db

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"slices"
	"strings"
	"time"

	"github.com/benbusby/farside/services"
	"github.com/robfig/cron/v3"
)

const defaultPrimary = "https://farside.link/state"
const defaultCFPrimary = "https://cf.farside.link/state"

var LastUpdate time.Time
var skipInstanceChecks = []string{
	"searx",
	"searxng",
}

func InitCronTasks() {
	log.Println("Initializing cron tasks...")
	updateServiceList()

	cronDisabled := os.Getenv("FARSIDE_CRON")
	if len(cronDisabled) == 0 || cronDisabled == "1" {
		c := cron.New()
		c.AddFunc("@every 10m", queryServiceInstances)
		c.AddFunc("@daily", updateServiceList)
		c.Start()
	}

	queryServiceInstances()
}

func updateServiceList() {
	fileName := services.GetServicesFileName()
	_, _ = services.FetchServicesFile(fileName)
	services.InitializeServices()
}

func queryServiceInstances() {
	log.Println("Starting instance queries...")

	isPrimary := os.Getenv("FARSIDE_PRIMARY")
	if len(isPrimary) == 0 || isPrimary != "1" {
		remoteServices, err := fetchInstancesFromPrimary()
		if err != nil {
			log.Println("Unable to fetch instances from primary", err)
		}

		for _, service := range remoteServices {
			SetInstances(service.Type, service.Instances)
		}

		LastUpdate = time.Now().UTC()
		return
	}

	for _, service := range services.ServiceList {
		canSkip := slices.Contains[[]string, string](skipInstanceChecks, service.Type)

		fmt.Printf("===== %s =====\n", service.Type)
		var instances []string
		for _, instance := range service.Instances {
			testURL := strings.ReplaceAll(
				service.TestURL,
				"<%=query%>",
				"current+weather")
			available := queryServiceInstance(
				instance,
				testURL,
				canSkip)

			if available {
				instances = append(instances, instance)
			}
		}

		SetInstances(service.Type, instances)
	}

	LastUpdate = time.Now().UTC()
}

func fetchInstancesFromPrimary() ([]services.Service, error) {
	primaryURL := defaultPrimary
	useCF := os.Getenv("FARSIDE_CF_ENABLED")
	if len(useCF) > 0 && useCF == "1" {
		primaryURL = defaultCFPrimary
	}

	resp, err := http.Get(primaryURL)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var serviceList []services.Service
	err = json.Unmarshal(bodyBytes, &serviceList)
	return serviceList, err
}

func queryServiceInstance(instance, testURL string, canSkipCheck bool) bool {
	testMode := os.Getenv("FARSIDE_TEST")
	if len(testMode) > 0 && testMode == "1" {
		return true
	}

	if canSkipCheck {
		fmt.Printf("    [INFO] Adding %s\n", instance)
		return true
	}

	ua := "Mozilla/5.0 (compatible; Farside/1.0.0; +https://farside.link)"
	url := instance + testURL

	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		fmt.Println("    [ERRO] Failed to create new http request!", err)
		return false
	}

	req.Header.Set("User-Agent", ua)
	client := &http.Client{
		Timeout: 10 * time.Second,
	}
	resp, err := client.Do(req)

	if err != nil {
		fmt.Println("    [ERRO] Error fetching instance:", err)
		return false
	} else if resp.StatusCode != http.StatusOK {
		fmt.Printf("    [WARN] Received non-200 status for %s\n", url)
		return false
	} else {
		fmt.Printf("    [INFO] Received 200 status for %s\n", url)
	}

	return true
}