// Copyright 2022 Paul Greenberg greenpau@outlook.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"net/http"
	urlpkg "net/url"
	"strings"
)

// GetBaseURL returns base path based on some match.
func GetBaseURL(r *http.Request, s string) (string, string) {
	for _, p := range strings.Split(s, ",") {
		i := strings.Index(r.URL.Path, p)
		if i >= 0 {
			return GetCurrentBaseURL(r), sanitizeURLPath(r.URL.Path[:i])
		}
	}
	return GetCurrentBaseURL(r), sanitizeURLPath(r.URL.Path)
}

// GetRelativeURL returns relative path to current URL.
func GetRelativeURL(r *http.Request, orig, repl string) string {
	i := strings.Index(r.URL.Path, orig)
	if i < 0 {
		return GetCurrentBaseURL(r) + repl
	}
	return GetCurrentBaseURL(r) + sanitizeURLPath(r.URL.Path[:i]) + repl
}

// sanitizeURLPath re-encodes each segment of a URL path so that characters
// unsafe in an HTML context (e.g. <, >, ", ') are percent-encoded while
// preserving forward slashes. Input is expected to be r.URL.Path, which
// net/http already decodes from the raw request, so no double-encoding
// occurs in practice.
func sanitizeURLPath(s string) string {
	parts := strings.Split(s, "/")
	for i, part := range parts {
		parts[i] = urlpkg.PathEscape(part)
	}
	return strings.Join(parts, "/")
}

// GetCurrentURL returns current URL with the path sanitized.
func GetCurrentURL(r *http.Request) string {
	return GetCurrentBaseURL(r) + sanitizeURLPath(r.URL.Path)
}

// GetIssuerURL returns issuer URL.
func GetIssuerURL(r *http.Request) string {
	s := GetCurrentURL(r)
	if !strings.HasSuffix(s, "callback") {
		return s
	}
	s = strings.TrimRightFunc(s, func(r rune) bool {
		if r == '/' {
			return false
		}
		return true
	})

	// i := strings.LastIndexByte(s, '/')
	// if i > 0 {
	//
	// }
	return s
}

// GetCurrentBaseURL returns current base URL.
func GetCurrentBaseURL(r *http.Request) string {
	redirHost := r.Header.Get("X-Forwarded-Host")
	if redirHost == "" {
		redirHost = r.Host
	}
	redirProto := r.Header.Get("X-Forwarded-Proto")
	if redirProto == "" {
		if r.TLS == nil {
			redirProto = "http"
		} else {
			redirProto = "https"
		}
	}
	redirPort := r.Header.Get("X-Forwarded-Port")
	redirectBaseURL := redirProto + "://" + redirHost

	if redirPort != "" {
		switch redirPort {
		case "443":
			if redirProto != "https" {
				redirectBaseURL += ":" + redirPort
			}
		case "80":
			if redirProto != "http" {
				redirectBaseURL += ":" + redirPort
			}
		default:
			redirectBaseURL += ":" + redirPort
		}
	}
	redirPrefix := r.Header.Get("X-Forwarded-Prefix")
	redirectBaseURL += redirPrefix

	return redirectBaseURL
}

// StripQueryParam removes a specific query parameter from a URL.
func StripQueryParam(url string, param string) string {
	u, err := urlpkg.Parse(url)
	if err != nil {
		return url
	}
	q := u.Query()
	q.Del(param)
	u.RawQuery = q.Encode()
	return u.String()
}
