package main

import (
	"flag"
	"fmt"
	"time"
	"net/http"
	"net/url"
	"io/ioutil"
	"strconv"
	"strings"
	"encoding/json"
)

var (
	httpPort = flag.Int("http_port", 80, "HTTP port to serve on (defaults to 80)")
)

type TokenResponse struct {
    AccessToken 			string    	`json:"access_token"`
	TokenType 				string      `json:"token_type"`
	ExpiresIn 				int      	`json:"expires_in"`
	RefreshToken 			string      `json:"refresh_token"`
	Scope 					string      `json:"scope"`
	RefreshTokenIssuedAt 	string      `json:"refresh_token_issued_at"`
	IssueAt 				string      `json:"issued_at"`
	UsagePointId			string      `json:"usage_points_id"`
}

func main() {
	// Parse the flags
	flag.Parse()
	mux := http.NewServeMux()
    fmt.Println("Server started")

	mux.HandleFunc("/auth_old", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("*******************")
        fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- New auth request")
		query := r.URL.Query()
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Query received - ", query)

		clientId := query.Get("client_id")
		cozyOrigin := query.Get("redirect_uri") // here we use the redirect_uri param to transmit our stack url
		state := query.Get("state")
		authReq := "https://gw.hml.api.enedis.fr/group/espace-particuliers/consentement-linky/oauth2/authorize?client_id="+ clientId +"&duration=P6M&redirect_uri=https://oauth-proxy.wf.alpha.grandlyon.com/&response_type=code&state="+ state +"-"+ cozyOrigin
		fmt.Println(authReq)
		response, err := http.Get(authReq)
		if err != nil {
			fmt.Println(err)
		} else {
			defer response.Body.Close()
			contents, err := ioutil.ReadAll(response.Body)
			if err != nil {
				fmt.Println(err)
			}
			// Get the response body as a string
			pageContent := string(contents)
		
			// Find a substr
			codeStartIndex := strings.Index(pageContent, "?code=")
			if codeStartIndex == -1 {
				fmt.Println("No code found")
			}
			codeStartIndex += 6
		
			// Find the index of the closing tag
			codeEndIndex := strings.Index(pageContent, "&state=")
			if codeEndIndex == -1 {
				fmt.Println("No closing tag for code found.")
			}

			stateStartIndex := strings.Index(pageContent, "&state=")
			if stateStartIndex == -1 {
				fmt.Println("No state found")
			}
			stateStartIndex += 7
		
			stateEndIndex := strings.Index(pageContent, "&usage_point_id=")
			if stateEndIndex == -1 {
				fmt.Println("No closing tag for state found.")
			}

			usageStartIndex := strings.Index(pageContent, "&usage_point_id=")
			if usageStartIndex == -1 {
				fmt.Println("No usage found")
			}
			usageStartIndex += 16
		
			usageEndIndex := strings.Index(pageContent, "&usage_point_id=")
			if usageEndIndex == -1 {
				fmt.Println("No closing tag for usage found.")
			}
			usageEndIndex += 30

			pageCode := string([]byte(pageContent[codeStartIndex:codeEndIndex]))
			pageState := string([]byte(pageContent[stateStartIndex:stateEndIndex]))
			pageUsage := string([]byte(pageContent[usageStartIndex:usageEndIndex]))

			// Print out the result
			fmt.Printf("Page code: %s\n", pageCode)
			fmt.Printf("Page state: %s\n", pageState)
			fmt.Printf("Page usage: %s\n", pageUsage)

			splitIndex := strings.Index(pageState, "-")
			if splitIndex == -1 {
				fmt.Println("No host found")
			}
			state := string([]byte(pageState[0:splitIndex]))
			encodedHost := string([]byte(pageState[splitIndex+1:len(pageState)]))
			host, err := url.QueryUnescape(encodedHost)
			if err != nil {
				fmt.Println("Host cannot be decoded")
			}
			
			redir := host + "?code=" + pageCode + "&state="+ state +"&usage_point_id=" + pageUsage
			fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Redirect to -", redir)
			http.Redirect(w, r, redir, 302)
		}
	})

	mux.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("*******************")
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- New auth request")
		query := r.URL.Query()
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Query received - ", query)

		clientId := query.Get("client_id")
		state := query.Get("state")
		cozyOrigin := query.Get("redirect_uri") // here we use the redirect_uri param to transmit our stack url
		redirectUri := "https://oauth-proxy.wf.alpha.grandlyon.com/redirect"
		responseType := "code" 

		authReq := "https://gw.hml.api.enedis.fr/group/espace-particuliers/consentement-linky/oauth2/authorize?client_id="+ clientId +"&duration=P6M&redirect_uri="+ redirectUri +"&response_type="+ responseType +"&state="+ state +"-"+ cozyOrigin

		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Send request to auth endpoint", authReq)
		response, err := http.Get(authReq)
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Endpoint response with status", response.Status)
		if err != nil {
			fmt.Println(err)
		} else {
			defer response.Body.Close()
			contents, err := ioutil.ReadAll(response.Body)
			if err != nil {
				fmt.Println(err)
			}
			// Define the replyUri
			// replyUri := "https://oauth-proxy.wf.alpha.grandlyon.com/redirect"
			// fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Reply to - ", replyUri)

			// Get the response body as a string
			pageContentString := string(contents)
			// Replace redirectUri by the host in the body
			// pageContentString = strings.ReplaceAll(pageContentString, redirectUri, replyUri)
			// Prevent the closure of the opener
			pageContentString = strings.ReplaceAll(pageContentString, "this.window.opener.location.href = url;", "this.window.location.href = url;")
			fmt.Println(pageContentString)
			// Convert string to byte for response writting
			newPageContentByte := []byte(pageContentString)

			// fmt.Println(r.Host)
			if response.StatusCode >= 200 && response.StatusCode <= 299 {
				w.Write(newPageContentByte)
			} else {
				http.Error(w, http.StatusText(response.StatusCode), response.StatusCode)
			}
		}
	})

	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("*******************")
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- New redirect request")
		query := r.URL.Query()
		fmt.Println(query)

		code := query.Get("code")
		req_state := query.Get("state")

		splitIndex := strings.Index(req_state, "-")
		if splitIndex == -1 {
			fmt.Println("No host found")
		}
		state := string([]byte(req_state[0:splitIndex]))
		host := string([]byte(req_state[splitIndex+1:len(req_state)]))

		usagePointId := query.Get("usage_point_id")

		redir := host + "?code=" + code + "&state="+ state +"&usage_point_id=" + usagePointId
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Redirect to -", redir)
		http.Redirect(w, r, redir, 302)
	})

	mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("*******************")
        fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- New token request")
		query := r.URL.Query()
		fmt.Println(query)

		clientId := query.Get("client_id")
		clientSecret := query.Get("client_secret")
		code := query.Get("code")
		grantType := query.Get("grant_type")
		refreshToken := query.Get("refresh_token")

		tokenUrl := "https://gw.hml.api.enedis.fr/v1/oauth2/token"
		
		data := url.Values{}
		data.Set("client_id", clientId)
		data.Set("client_secret", clientSecret)
		data.Set("code", code)
		data.Set("grant_type", grantType)
		if refreshToken != "" {
			data.Set("refresh_token", refreshToken)
			data.Set("grant_type", "refresh_token")
		}
		
		client := &http.Client{}
		req, _ := http.NewRequest("POST", tokenUrl, strings.NewReader(data.Encode()))
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Send request to token endpoint", tokenUrl)
		response, err := client.Do(req)
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "- Endpoint response with status", response.Status)
		if err != nil {
			fmt.Println(err)
		} else {
			defer response.Body.Close()
			if response.StatusCode >= 200 && response.StatusCode <= 299 {
				// Set Content-Type in response header
				w.Header().Add("Content-Type", "application/json")

				// Decode response Body using the defined type "TokenResponse"
				data := TokenResponse{}
				decodeError := json.NewDecoder(response.Body).Decode(&data)
				if decodeError != nil {
					http.Error(w, decodeError.Error(), 500)
					return
				}
				
				// Response with json data
				jsonError := json.NewEncoder(w).Encode(data)
				if jsonError != nil {
					http.Error(w, jsonError.Error(), 500)
					return
				}
			} else {
				http.Error(w, http.StatusText(response.StatusCode), response.StatusCode)
			}
		}
	})

	http.ListenAndServe(":"+strconv.Itoa(*httpPort), mux)
}