package handlers

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"github.com/gesellix/bose-soundtouch/pkg/service/proxy"
)

// HandleProxyRequest handles requests to the logging proxy.
func (s *Server) HandleProxyRequest(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("X-Bose-Proxy-Hop") != "" {
		log.Printf("[PROXY_LOOP] Loop detected for %s %s, breaking loop", r.Method, r.URL.Path)
		http.Error(w, "Loop detected", http.StatusNotFound)

		return
	}

	targetURLStr := strings.TrimPrefix(r.URL.Path, "/proxy/")
	if targetURLStr == "" {
		http.Error(w, "Target URL is required", http.StatusBadRequest)
		return
	}

	// Reconstruct original URL (it might have lost its double slashes in the path)
	if !strings.HasPrefix(targetURLStr, "http://") && !strings.HasPrefix(targetURLStr, "https://") {
		// Try to fix it if it looks like http:/...
		if strings.HasPrefix(targetURLStr, "http:/") {
			targetURLStr = "http://" + strings.TrimPrefix(targetURLStr, "http:/")
		} else if strings.HasPrefix(targetURLStr, "https:/") {
			targetURLStr = "https://" + strings.TrimPrefix(targetURLStr, "https:/")
		}
	}

	target, err := url.Parse(targetURLStr)
	if err != nil {
		http.Error(w, "Invalid target URL: "+err.Error(), http.StatusBadRequest)
		return
	}

	s.ServeProxy(target)(w, r)
}

// ServeProxy returns a handler that proxies to the given target.
func (s *Server) ServeProxy(target *url.URL) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		lp := proxy.NewLoggingProxy(target.String(), s.proxyRedact)
		lp.LogBody = s.proxyLogBody
		lp.RecordEnabled = s.recordEnabled
		lp.SetRecorder(s.recorder)

		// Capture request body for recording, as it will be consumed by the proxy
		var reqBody []byte
		if r.Body != nil {
			reqBody, _ = io.ReadAll(r.Body)
			r.Body = io.NopCloser(bytes.NewBuffer(reqBody))
		}

		rp := &httputil.ReverseProxy{
			Rewrite: func(pr *httputil.ProxyRequest) {
				pr.SetURL(target)
				pr.Out.Host = target.Host
				// If target has a path, we should probably append or replace.
				// For Bose upstream, it's usually just the domain.
				if target.Path != "" && target.Path != "/" {
					pr.Out.URL.Path = target.Path
				}

				pr.Out.Header.Set("X-Bose-Proxy-Hop", "1")

				lp.LogRequest(pr.Out)
			},
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		}

		rp.ModifyResponse = func(res *http.Response) error {
			res.Header.Set("X-Proxy-Origin", "upstream")
			// Generic Header Preservation
			if etags, ok := res.Header["Etag"]; ok {
				delete(res.Header, "Etag")
				res.Header["ETag"] = etags
			}

			// Restore captured request body for the recorder
			if reqBody != nil {
				res.Request.Body = io.NopCloser(bytes.NewBuffer(reqBody))
			}

			lp.LogResponse(res)

			return nil
		}

		rp.ServeHTTP(w, r)
	}
}

// HandleNotFound handles requests that don't match any route.
// It always logs [UNHANDLED] so unimplemented endpoints are visible in plain output.
// When proxyLogBody is enabled it also logs the request body (truncated to 512 bytes).
func (s *Server) HandleNotFound(w http.ResponseWriter, r *http.Request) {
	if s.proxyLogBody && r.Body != nil {
		body, _ := io.ReadAll(r.Body)
		r.Body = io.NopCloser(bytes.NewBuffer(body))

		preview := body
		truncated := ""

		if len(preview) > 512 {
			preview = preview[:512]
			truncated = "…"
		}

		log.Printf("[UNHANDLED] %s %s body(%d bytes): %s%s", r.Method, r.URL.Path, len(body), preview, truncated)
	} else {
		log.Printf("[UNHANDLED] %s %s", r.Method, r.URL.Path)
	}

	s.HandleBoseProxy(w, r)
}

// HandleBoseProxy proxies the request to the Bose upstream.
func (s *Server) HandleBoseProxy(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("X-Bose-Proxy-Hop") != "" {
		log.Printf("[PROXY_LOOP] Loop detected for %s %s, breaking loop", r.Method, r.URL.Path)
		http.Error(w, "Loop detected", http.StatusNotFound)

		return
	}

	host := r.Host
	if host == "" {
		host = "streaming.bose.com"
	}

	// Default to HTTPS for Bose services
	scheme := "https"
	if strings.HasPrefix(host, "localhost") || strings.HasPrefix(host, "127.0.0.1") || strings.HasPrefix(host, "::1") {
		scheme = "http"
	}

	targetURL := scheme + "://" + host

	target, err := url.Parse(targetURL)
	if err != nil {
		log.Printf("[PROXY_ERR] Failed to parse target URL %s: %v", targetURL, err)
		http.Error(w, "Invalid upstream host", http.StatusBadGateway)

		return
	}

	s.ServeProxy(target)(w, r)
}
