/*
Copyright © 2025 cavaliba.com
*/
package poller

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"time"

	"github.com/spf13/viper"
)

var httpClient *http.Client

type CheckResult struct {
	Keyname  string        `json:"keyname"`
	Name     string        `json:"name"`
	Status   bool          `json:"status"`
	HTTPCode int           `json:"http_code"`
	Duration time.Duration `json:"duration"`
	Error    string        `json:"error"`
}

// InitHTTPClient initializes the HTTP client with Viper configuration
func InitHTTPClient() {
	timeout := time.Duration(viper.GetInt("timeout")) * time.Millisecond
	sslSkipVerify := viper.GetBool("ssl_skipverify")

	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: sslSkipVerify},
	}

	httpClient = &http.Client{
		Transport: transport,
		Timeout:   timeout,
	}
}

// SingleCheck checks a single Monitor
func SingleCheck(monitor Monitor) CheckResult {

	verbose := viper.GetBool("verbose")
	var result CheckResult

	result.Keyname = monitor.Keyname
	result.Name = monitor.Name
	result.Status = false

	if verbose {
		fmt.Printf("\nChecking: %s (%s)\n", monitor.Name, monitor.Keyname)
		fmt.Printf("    URL: %s\n", monitor.HTTPURL)
		fmt.Printf("    Expected HTTP code: %d\n", monitor.HTTPCode)
		if monitor.HTTPPattern != "" {
			fmt.Printf("    Pattern: %s\n", monitor.HTTPPattern)
		}
		fmt.Printf("    Timeout: %v\n", monitor.Timeout)
	}

	// Initialize HTTP client if not already done
	if httpClient == nil {
		InitHTTPClient()
	}

	timeout := monitor.Timeout
	ctx, cancel := context.WithTimeout(context.Background(),
		timeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx,
		http.MethodGet,
		monitor.HTTPURL,
		nil)
	if err != nil {
		result.Error = fmt.Sprintf("bad URL/method (%v)", err)
		if verbose {
			fmt.Printf("    Error: %s\n", result.Error)
		}
		return result
	}

	start := time.Now()
	resp, err := httpClient.Do(req)
	result.Duration = time.Since(start)

	if err != nil {
		result.Error = fmt.Sprintf("http request failed (%v)", err)
		if verbose {
			fmt.Printf("    Error: %s\n", result.Error)
		}
		return result
	}
	defer resp.Body.Close()

	//expected_status := http.StatusOK
	result.HTTPCode = resp.StatusCode
	if verbose {
		fmt.Printf("    HTTP code: %d (duration: %v)\n", result.HTTPCode, result.Duration)
	}

	if resp.StatusCode != monitor.HTTPCode {
		result.Error = fmt.Sprintf("unexpected http status code (status: %d)", resp.StatusCode)
		if verbose {
			fmt.Printf("    Error: %s\n", result.Error)
		}
		return result
	}

	//fmt.Println(resp.Header.Get("Content-Type"))

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		//log.Println(err)
		result.Error = fmt.Sprintf("error reading response body (%v)", err)
		if verbose {
			fmt.Printf("    Error: %s\n", result.Error)
		}
		return result
	}
	bodyString := string(bodyBytes)

	if verbose {
		bodyLen := len(bodyBytes)
		fmt.Printf("    Response body size: %d bytes\n", bodyLen)
	}

	// Check pattern matching
	patternStr := monitor.HTTPPattern
	if patternStr == "" {
		// No pattern specified, status check passed
		result.Status = true
		if verbose {
			fmt.Printf("    Pattern check: PASS (no pattern specified)\n")
		}
	} else if patternStr[0] == '!' {
		// Negative pattern: must NOT match
		patternStr = patternStr[1:] // Strip the '!'
		pattern, err := regexp.Compile(patternStr)
		if err == nil && !pattern.MatchString(bodyString) {
			result.Status = true
			if verbose {
				fmt.Printf("    Pattern check: PASS (reject pattern '%s' not found)\n", patternStr)
			}
		} else {
			result.Error = fmt.Sprintf("reject pattern found (%s)", patternStr)
			if verbose {
				fmt.Printf("    Pattern check: FAIL (%s)\n", result.Error)
			}
		}
	} else {
		// Positive pattern: must match
		pattern, err := regexp.Compile(patternStr)
		if err == nil && pattern.MatchString(bodyString) {
			result.Status = true
			if verbose {
				fmt.Printf("    Pattern check: PASS (pattern '%s' found)\n", patternStr)
			}
		} else {
			result.Error = fmt.Sprintf("expected pattern not found (%s)", patternStr)
			if verbose {
				fmt.Printf("    Pattern check: FAIL (%s)\n", result.Error)
			}
		}
	}

	if verbose {
		status := "FAIL"
		if result.Status {
			status = "PASS"
		}
		fmt.Printf("    Result: %s\n", status)
	}

	return result
}
