package proxy

import (
	"crypto/tls"
	"log/slog"
	"net/http"
	"net/http/httputil"
	"net/url"

	"yobble-gateway-go/internal/config"
	"yobble-gateway-go/internal/logger"
	"yobble-gateway-go/internal/middleware"
	"yobble-gateway-go/pkg/geoip"
)

// NewProxyHandler создает новый HTTP-обработчик, который выполняет обратное проксирование запросов.
func NewProxyHandler(cfg *config.Settings, geoIPService *geoip.GeoIPService) http.Handler {
	op := "proxy.NewProxyHandler"
	log := logger.NewLoggerWithOp(op)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Проверка GeoIP
		realIP := middleware.GetRealIP(r)
		if realIP != "" {
			isBlocked, countryCode := geoIPService.IsCountryBlocked(realIP)
			if isBlocked {
				log.Warn("access denied due to blocked country", slog.String("ip", realIP), slog.String("country_code", countryCode))
				http.Error(w, "Access denied due to service policy.\n", http.StatusForbidden)
				return
			}
		}

		backend, tailPath := ResolveBackend(r.URL.Path, cfg.RouteConfig)

		if backend == nil {
			log.Warn("no route found for path", slog.String("path", r.URL.Path))
			http.Error(w, "Route not found", http.StatusNotFound)
			return
		}

		backendURL, err := url.Parse(backend.URL)
		if err != nil {
			log.Error("failed to parse backend URL", slog.String("url", backend.URL), slog.Any("error", err))
			http.Error(w, "Upstream error", http.StatusBadGateway)
			return
		}

		// Создаем обратный прокси
		proxy := httputil.NewSingleHostReverseProxy(backendURL)

		// Изменяем запрос, который будет отправлен на бэкенд
		originalDirector := proxy.Director
		proxy.Director = func(req *http.Request) {
			originalDirector(req) // Устанавливает базовые заголовки и URL

			// Устанавливаем правильный путь для бэкенда
			req.URL.Path = tailPath

			// Устанавливаем заголовки для информирования бэкенда об исходном запросе
			req.Header.Set("X-Real-IP", realIP)
			req.Header.Set("X-Forwarded-For", realIP)
			req.Header.Set("X-Forwarded-Host", r.Host)
			req.Header.Set("X-Forwarded-Proto", r.URL.Scheme)
			// Удаляем заголовки, которые могут раскрыть информацию о внутреннем сервере
			req.Header.Del("Server")
			req.Header.Del("X-Powered-By")
		}

		// Изменяем ответ от бэкенда перед отправкой клиенту
		proxy.ModifyResponse = func(resp *http.Response) error {
			resp.Header.Del("Server")
			resp.Header.Del("X-Powered-By")
			return nil
		}

		// Пользовательский транспорт для обработки allow_self_signed
		if backend.AllowSelfSigned {
			proxy.Transport = &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			}
		}

		log.Info("forwarding request", slog.String("from", r.URL.Path), slog.String("to_backend", backendURL.String()), slog.String("with_path", tailPath))
		proxy.ServeHTTP(w, r)
	})
}
