package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/netip"
	"os"

	"golang.org/x/oauth2"
	"google.golang.org/api/drive/v2"
	"sirherobrine23.com.br/Sirherobrine23/drivefs"
)

var (
	configPath = flag.String("config", "./config.json", "Config file path")
	serverPort = flag.Uint("port", 8081, "server to listen")
	setupAuth  = flag.Bool("auth", false, "Listen server and Auth")

	client        = flag.String("client", "", "installed.client_id")
	secret        = flag.String("secret", "", "installed.client_secret")
	project       = flag.String("project", "", "installed.project_id")
	auth_uri      = flag.String("auth_uri", "", "installed.auth_uri")
	token_uri     = flag.String("token_uri", "", "installed.token_uri")
	redirect      = flag.String("redirect", "", "installed.redirect_uris[]")
	access_token  = flag.String("access_token", "", "token.access_token")
	refresh_token = flag.String("refresh_token", "", "token.refresh_token")
	token_type    = flag.String("token_type", "", "token.token_type")
	root_folder   = flag.String("root_folder", "", "Google drive folder id (gdrive:<ID>) or path to folder")

	gdriveConfig drivefs.GoogleOauthConfig
)

func main() {
	flag.Parse()
	gdriveConfig.Client = *client
	gdriveConfig.Secret = *secret
	gdriveConfig.Project = *project
	gdriveConfig.AuthURI = *auth_uri
	gdriveConfig.TokenURI = *token_uri
	gdriveConfig.Redirect = *redirect
	gdriveConfig.AccessToken = *access_token
	gdriveConfig.RefreshToken = *refresh_token
	gdriveConfig.TokenType = *token_type
	gdriveConfig.RootFolder = *root_folder

	fileConfig, err := os.ReadFile(*configPath)
	if err == nil {
		if err = json.Unmarshal(fileConfig, &gdriveConfig); err != nil {
			fmt.Fprintf(os.Stderr, "Cannot unmarshall config: %s\n", err)
			os.Exit(1)
			return
		}
	} else if os.IsNotExist(err) {
	} else {
		fmt.Fprintf(os.Stderr, "Cannot open %q: %s\n", *configPath, err)
		os.Exit(1)
		return
	}

	if *setupAuth {
		ln, err := net.Listen("tcp", ":0")
		if err != nil {
			panic(err)
		}
		P, _ := netip.ParseAddrPort(ln.Addr().String())
		ln.Close()

		config := &oauth2.Config{
			ClientID:     gdriveConfig.Client,
			ClientSecret: gdriveConfig.Secret,
			RedirectURL:  fmt.Sprintf("http://localhost:%d/callback", P.Port()),
			Scopes:       []string{drive.DriveScope, drive.DriveFileScope},
			Endpoint: oauth2.Endpoint{
				AuthURL:  gdriveConfig.AuthURI,
				TokenURL: gdriveConfig.TokenURI,
			},
		}

		var (
			server      *http.Server
			GoogleToken *oauth2.Token
		)

		mux := http.NewServeMux()
		mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
			authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
			http.Redirect(w, r, authURL, http.StatusTemporaryRedirect)
		})

		mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "text/plain")
			if code := r.URL.Query().Get("code"); code != "" {
				if GoogleToken, err = config.Exchange(context.TODO(), code); err != nil {
					panic(fmt.Errorf("unable to retrieve token from web %v", err))
				}

				defer server.Close()
				w.WriteHeader(200)
				fmt.Fprintf(w, "<html><body>Code: %q</body></html>", code)
				fmt.Printf("Code: %q\n", code)
				return
			}
			w.WriteHeader(400)
			w.Write([]byte("Wait to code\n"))
		})

		fmt.Printf("Go to the following link in your browser then type the authorization code: \nhttp://%s/token\n", P.String())
		server = &http.Server{Addr: P.String(), Handler: mux}
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			panic(err)
		}

		gdriveConfig.AccessToken = GoogleToken.AccessToken
		gdriveConfig.RefreshToken = GoogleToken.RefreshToken
		gdriveConfig.TokenType = GoogleToken.TokenType
		gdriveConfig.Expire = GoogleToken.Expiry

		data, err := json.MarshalIndent(gdriveConfig, "", "  ")
		if err != nil {
			panic(err)
		} else if err = os.WriteFile(*configPath, data, 0666); err != nil {
			panic(err)
		}
	}

	gdrive, err := drivefs.NewGoogleDrive(gdriveConfig)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot open gdrive client: %s\n", err)
		os.Exit(1)
		return
	}

	fmt.Printf("server listening on :%d\n", *serverPort)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", *serverPort), http.FileServerFS(gdrive)); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

}