package proxy

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"time"
)

// TraefikProvider manages proxy routes via Docker labels.
// Traefik auto-discovers containers with the appropriate labels.
type TraefikProvider struct {
	entrypoint   string
	certResolver string
	network      string // Docker network for traefik.docker.network label
	apiURL       string // Traefik API URL for health checks (optional)
	httpClient   *http.Client
}

// NewTraefikProvider creates a Traefik-backed proxy provider.
func NewTraefikProvider(entrypoint, certResolver, network, apiURL string) *TraefikProvider {
	if entrypoint == "" {
		entrypoint = "websecure"
	}
	return &TraefikProvider{
		entrypoint:   entrypoint,
		certResolver: certResolver,
		network:      network,
		apiURL:       strings.TrimRight(apiURL, "/"),
		httpClient:   &http.Client{Timeout: 5 * time.Second},
	}
}

func (t *TraefikProvider) Name() string { return "traefik" }

// ConfigureRoute for Traefik is a no-op for deploy-managed containers.
// Labels are set at container creation time via ContainerLabels().
// Returns a route ID for tracking.
func (t *TraefikProvider) ConfigureRoute(_ context.Context, domain, _ string, _ int, _ RouteOptions) (string, error) {
	routerName := sanitizeDomain(domain)
	return routerName, nil
}

// DeleteRoute for Traefik is a no-op — removing the container removes the labels,
// and Traefik automatically de-registers the route.
func (t *TraefikProvider) DeleteRoute(_ context.Context, _ string) error {
	return nil
}

// ContainerLabels returns Docker labels for Traefik auto-discovery.
func (t *TraefikProvider) ContainerLabels(domain string, port int) map[string]string {
	name := sanitizeDomain(domain)
	labels := map[string]string{
		"traefik.enable": "true",
		fmt.Sprintf("traefik.http.routers.%s.rule", name):                     fmt.Sprintf("Host(`%s`)", domain),
		fmt.Sprintf("traefik.http.routers.%s.entrypoints", name):              t.entrypoint,
		fmt.Sprintf("traefik.http.services.%s.loadbalancer.server.port", name): fmt.Sprintf("%d", port),
	}
	if t.certResolver != "" {
		labels[fmt.Sprintf("traefik.http.routers.%s.tls.certresolver", name)] = t.certResolver
	}
	if t.network != "" {
		labels["traefik.docker.network"] = t.network
	}
	return labels
}

// Ping checks Traefik API connectivity if a URL is configured.
func (t *TraefikProvider) Ping(ctx context.Context) error {
	if t.apiURL == "" {
		return nil // No API URL configured, skip health check.
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, t.apiURL+"/api/overview", nil)
	if err != nil {
		return fmt.Errorf("create traefik ping request: %w", err)
	}
	resp, err := t.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("traefik ping: %w", err)
	}
	resp.Body.Close()
	if resp.StatusCode >= 400 {
		return fmt.Errorf("traefik api returned status %d", resp.StatusCode)
	}
	return nil
}

// sanitizeDomain converts a domain to a safe Traefik router name.
func sanitizeDomain(domain string) string {
	r := strings.NewReplacer(".", "-", ":", "-", "*", "wildcard")
	return r.Replace(strings.ToLower(domain))
}
