package services

import (
	"fmt"
	"log"
	"strings"
)

// OTP delivery, shared by both stacks.
//
// Two rules hold everywhere an OTP goes out:
//
//   - Nothing is dispatched outside production. A development or QA build
//     issues the static code and never touches the carrier, so a test run
//     costs no SMS credit and cannot spam a real handset.
//
//   - Delivery is best effort and always happens after the OTP row is
//     committed. A carrier outage must not roll back a registration, a login
//     or a password reset: the code is already stored, the caller can ask for
//     it again with /resend-otp, and failing the whole request over an SMS
//     turned every gateway hiccup into "registration failed".
//
// The user-facing services used to return the MSG91 error straight out of
// RegisterUser, Login, ResendOTP and ForgotPassword, which is what made an
// empty auth key look like a broken signup.

// SendMobileOTP dispatches one OTP over the SMS gateway, unconditionally.
//
// Callers that want the environment and the kill switch respected should use
// DeliverMobileOTP instead. This one exists for the paths that have already
// decided to send — the astrologer stack's delivery helper and the
// command-line test tool.
func SendMobileOTP(mobile string, otp string) error {

	mobile = strings.TrimSpace(mobile)

	if mobile == "" {
		return fmt.Errorf("mobile number is required")
	}

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

	if err != nil {
		return err
	}

	if response != nil && !response.Success {
		return fmt.Errorf("MSG91 rejected the message: %s", response.RawResponse)
	}

	return nil
}

// DeliverMobileOTP sends the mobile OTP and does nothing outside production or
// while the gateway is switched off. Failures are logged, never returned.
func DeliverMobileOTP(mobile string, otp string) {

	if strings.TrimSpace(mobile) == "" || strings.TrimSpace(otp) == "" {
		return
	}

	if !IsProduction() {
		return
	}

	if !ResolveMSG91Config().Enabled {
		log.Printf("otp: sms gateway is disabled (%s = 0), skipping delivery to %s", FlagSmsGatewayEnabled, maskMobile(mobile))
		return
	}

	if err := SendMobileOTP(mobile, otp); err != nil {
		log.Printf("otp: mobile delivery failed for %s: %v", maskMobile(mobile), err)
	}
}

// DeliverEmailOTP sends the email OTP on the same best-effort terms.
//
// SmsGatewayEnabled switches this off too. The flag is named for SMS because
// that is what it was added for, but it is the OTP delivery kill switch: with
// it set to 0 the SMS was correctly skipped while the OTPLESS call still went
// out, which is not what "stop sending" means to whoever sets it during an
// outage.
func DeliverEmailOTP(email string, otp string) {

	if strings.TrimSpace(email) == "" || strings.TrimSpace(otp) == "" {
		return
	}

	if !IsProduction() {
		return
	}

	if !ResolveMSG91Config().Enabled {
		log.Printf("otp: delivery is disabled (%s = 0), skipping email to %s", FlagSmsGatewayEnabled, email)
		return
	}

	if err := SendEmailOTP(email, otp); err != nil {
		log.Printf("otp: email delivery failed for %s: %v", email, err)
	}
}

// maskMobile keeps the log useful without writing a full number into it.
func maskMobile(mobile string) string {

	mobile = strings.TrimSpace(mobile)

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

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