package main

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/gmail/v1"
	"google.golang.org/api/option"
)

const (
	TOKEN_FILE       = "token.json"
	CREDENTIALS_FILE = "credentials.json"
)

// Returns the generated client.
func getClient(config *oauth2.Config) (*http.Client, error) {
	tok, err := tokenFromFile(TOKEN_FILE)
	if err != nil {
		return nil, fmt.Errorf("unable to retrieve token from file: %v", err)
	}
	return config.Client(context.Background(), tok), nil
}

// Request a token from the web, then returns the retrieved token.
func getTokenFromWeb(config *oauth2.Config) (*oauth2.Token, error) {
	authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
	fmt.Printf("Go to the following link in your browser then type the "+
		"authorization code: \n%v\n\n", authURL)

	fmt.Print("Enter the authorization code: ")
	var authCode string
	if _, err := fmt.Scan(&authCode); err != nil {
		return nil, fmt.Errorf("unable to read authorization code: %v", err)
	}

	tok, err := config.Exchange(context.TODO(), authCode)
	if err != nil {
		return nil, fmt.Errorf("unable to retrieve token from web: %v", err)
	}
	return tok, nil
}

// Retrieves a token from a local file.
func tokenFromFile(file string) (*oauth2.Token, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	tok := &oauth2.Token{}
	err = json.NewDecoder(f).Decode(tok)
	return tok, err
}

// Saves a token to a file path.
func saveToken(path string, token *oauth2.Token) error {
	fmt.Printf("Saving credential file to: %s\n", path)
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("unable to cache oauth token: %v", err)
	}
	defer f.Close()
	json.NewEncoder(f).Encode(token)
	return nil
}

// Retrieve a token, saves the token
// The file token.json stores the user's access and refresh tokens
func initMailer() error {
	if _, err := os.Stat(TOKEN_FILE); err == nil {
		fmt.Println("Token file already exists. Proceeding")
		return nil
	}
	fmt.Println("Token file not found. Generating new token")

	b, err := os.ReadFile(CREDENTIALS_FILE)
	if err != nil {
		return fmt.Errorf("unable to read client secret file: %v", err)
	}

	// If modifying these scopes, delete your previously saved token.json.
	config, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
	if err != nil {
		return fmt.Errorf("unable to parse client secret file to config: %v", err)
	}

	token, err := getTokenFromWeb(config)
	if err != nil {
		return fmt.Errorf("unable to retrieve token from web: %v", err)
	}

	err = saveToken(TOKEN_FILE, token)
	if err != nil {
		return fmt.Errorf("unable to save token: %v", err)
	}

	return nil
}

func sendMail(receiverEmail string, subject string, body string) (bool, error) {
	ctx := context.Background()
	b, err := os.ReadFile(CREDENTIALS_FILE)
	if err != nil {
		return false, fmt.Errorf("unable to read client secret file: %v", err)
	}

	// If modifying these scopes, delete your previously saved token.json.
	config, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
	if err != nil {
		return false, fmt.Errorf("unable to parse client secret file to config: %v", err)
	}

	client, err := getClient(config)
	if err != nil {
		return false, fmt.Errorf("unable to get client: %v", err)
	}

	srv, err := gmail.NewService(ctx, option.WithHTTPClient(client))
	if err != nil {
		return false, fmt.Errorf("unable to retrieve Gmail client: %v", err)
	}

	var message gmail.Message

	msgStr := fmt.Sprintf("From: 'me'\r\nTo: %s\r\nSubject: %s\r\n\r\n%s", receiverEmail, subject, body)
	message.Raw = base64.URLEncoding.EncodeToString([]byte(msgStr))

	userID := "me"
	_, err = srv.Users.Messages.Send(userID, &message).Do()
	if err != nil {
		return false, fmt.Errorf("unable to send message: %v", err)
	}

	return true, nil
}

func sendOTP(receiverEmail string, otp string) (bool, error) {
	subject := "Metakgp verification OTP"
	body := "OTP for Sign In into Heimdall Portal of Metakgp, IIT Kharagpur is " + otp

	return sendMail(receiverEmail, subject, body)
}