package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	config "astrology-api/configs"
	"astrology-api/services"
)

// A standalone check of the SMS gateway and the OTP environment switch, without
// having to drive a registration or a login to find out what the API would do.
//
// Three modes:
//
//	go run ./cmd/smstest -inspect
//	    Prints the resolved MSG91 configuration, where each value came from,
//	    and whether this deployment would send a real SMS or issue the static
//	    OTP. Sends nothing.
//
//	go run ./cmd/smstest -otp
//	    Prints the OTP the current environment would issue, five times, so a
//	    production build can be seen to produce different codes and a
//	    development one the static code.
//
//	go run ./cmd/smstest -mobile 9876543210
//	    Sends one real SMS through the gateway, regardless of APP_ENV. This is
//	    the only mode that spends an SMS credit.
//
// All three need .env and a reachable MySQL, because the credentials live in
// the systemflag table. Exit status is 0 only on success, so this can be used
// in a deployment check.
func main() {

	inspect := flag.Bool("inspect", false, "print the resolved gateway configuration and send nothing")

	showOTP := flag.Bool("otp", false, "print the OTP this environment would issue")

	mobile := flag.String("mobile", "", "mobile number to send one real test SMS to")

	otpValue := flag.String("code", "123456", "the code to put in the test SMS, with -mobile")

	flag.Parse()

	config.LoadEnv()
	config.ConnectDB()

	// The resolver caches for a minute; a test run should always see the
	// current rows.
	services.ResetSMSConfigCache()

	switch {

	case *mobile != "":
		sendTestSMS(*mobile, *otpValue)

	case *showOTP:
		printOTPs()

	case *inspect:
		printConfig()

	default:
		printConfig()
		fmt.Println()
		fmt.Println("Nothing was sent. Use -mobile <number> to send a real test SMS.")
	}
}

func printConfig() {

	cfg := services.ResolveMSG91Config()

	fmt.Println("MSG91 configuration")
	fmt.Println("-------------------")
	fmt.Printf("  auth key           %s %s\n", mask(cfg.AuthKey), source(services.FlagMsg91AuthKey, "MSG91_AUTH_KEY"))
	fmt.Printf("  api url            %s %s\n", cfg.APIURL, source(services.FlagMsg91ApiURL, "MSG91_API_URL"))
	fmt.Printf("  sender id          %s %s\n", cfg.Sender, source(services.FlagMsg91SenderID, "MSG91_SENDER_ID"))
	fmt.Printf("  dlt template id    %s %s\n", cfg.DLTTemplateID, source(services.FlagMsg91OtpTemplateID, "MSG91_OTP_TEMPLATE_ID"))
	fmt.Printf("  dlt header id      %s %s\n", orNone(cfg.HeaderID), source(services.FlagMsg91HeaderID, "MSG91_HEADER_ID"))
	fmt.Printf("  dlt pe id          %s %s\n", orNone(cfg.PeID), source(services.FlagMsg91PeID, "MSG91_PE_ID"))
	fmt.Printf("  route              %s %s\n", cfg.Route, source(services.FlagMsg91Route, "MSG91_ROUTE"))
	fmt.Printf("  country            %s %s\n", cfg.Country, source(services.FlagMsg91Country, "MSG91_COUNTRY"))
	fmt.Printf("  otp length         %d\n", cfg.OTPLength)
	fmt.Printf("  otp expiry         %d minutes\n", cfg.OTPExpiryMinutes)
	fmt.Printf("  gateway enabled    %t\n", cfg.Enabled)

	fmt.Println()
	fmt.Println("OTP behaviour")
	fmt.Println("-------------")
	fmt.Printf("  APP_ENV            %q\n", strings.TrimSpace(os.Getenv("APP_ENV")))
	fmt.Printf("  production         %t\n", services.IsProduction())

	if services.IsProduction() && cfg.Enabled {
		fmt.Println("  behaviour          a fresh random OTP is issued and sent over MSG91")
	} else if services.IsProduction() {
		fmt.Printf("  behaviour          a fresh random OTP is issued, nothing is sent (%s = 0)\n", services.FlagSmsGatewayEnabled)
	} else {
		fmt.Printf("  behaviour          the static OTP %q is issued, nothing is sent\n", services.StaticOTPCode())
	}

	if strings.TrimSpace(cfg.AuthKey) == "" {
		fmt.Println()
		fmt.Printf("  WARNING: no auth key. Every send will fail. Set the %s flag or MSG91_AUTH_KEY.\n", services.FlagMsg91AuthKey)
	}
}

func printOTPs() {

	fmt.Printf("APP_ENV=%q production=%t\n\n", strings.TrimSpace(os.Getenv("APP_ENV")), services.IsProduction())

	for i := 0; i < 5; i++ {

		otp, err := services.NewOTP()

		if err != nil {
			fmt.Printf("  error: %v\n", err)
			os.Exit(1)
		}

		fmt.Printf("  %s\n", otp)
	}
}

func sendTestSMS(mobile string, otp string) {

	cfg := services.ResolveMSG91Config()

	fmt.Printf("sending %q to %s via %s (sender %s, route %s)\n\n", otp, mobile, cfg.APIURL, cfg.Sender, cfg.Route)

	response, err := services.NewMSG91Service().SendMobileOTP(mobile, otp)

	if err != nil {
		fmt.Printf("FAILED: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("  http status  %d\n", response.StatusCode)
	fmt.Printf("  accepted     %t\n", response.Success)
	fmt.Printf("  response     %s\n", response.RawResponse)

	if !response.Success {
		os.Exit(1)
	}
}

// source says where a value was resolved from, which is the question a
// deployment that "did not pick up the new key" actually needs answered.
func source(flag string, envName string) string {

	if value := services.SystemFlagRawValue(flag); value != "" {
		return "(systemflag " + flag + ")"
	}

	if strings.TrimSpace(os.Getenv(envName)) != "" {
		return "(env " + envName + ")"
	}

	return "(built-in default)"
}

func mask(value string) string {

	value = strings.TrimSpace(value)

	if value == "" {
		return "<empty>"
	}

	if len(value) <= 4 {
		return "****"
	}

	return strings.Repeat("*", len(value)-4) + value[len(value)-4:]
}

func orNone(value string) string {

	if strings.TrimSpace(value) == "" {
		return "<not set>"
	}

	return value
}
