package proxy

import (
	"context"
	"fmt"
	"strconv"

	"github.com/alexei/docker-watcher/internal/npm"
)

// NpmProvider wraps the NPM client behind the Provider interface.
// It handles authentication transparently before each operation.
type NpmProvider struct {
	client   *npm.Client
	email    string
	password string
}

// NewNpmProvider creates an NPM-backed proxy provider.
// The email and password are the decrypted NPM credentials.
func NewNpmProvider(client *npm.Client, email, password string) *NpmProvider {
	return &NpmProvider{
		client:   client,
		email:    email,
		password: password,
	}
}

// UpdateCredentials updates the stored NPM credentials (e.g., after settings change).
func (p *NpmProvider) UpdateCredentials(email, password string) {
	p.email = email
	p.password = password
}

func (p *NpmProvider) Name() string { return "npm" }

func (p *NpmProvider) ConfigureRoute(ctx context.Context, domain, targetHost string, targetPort int, opts RouteOptions) (string, error) {
	if err := p.auth(ctx); err != nil {
		return "", err
	}

	scheme := opts.ForwardScheme
	if scheme == "" {
		scheme = "http"
	}

	// Check if a proxy host already exists for this domain.
	existing, found, err := p.client.FindProxyHostByDomain(ctx, domain)
	if err != nil {
		return "", fmt.Errorf("find existing proxy host: %w", err)
	}

	config := npm.ProxyHostConfig{
		DomainNames:    []string{domain},
		ForwardScheme:  scheme,
		ForwardHost:    targetHost,
		ForwardPort:    targetPort,
		BlockExploits:  true,
		AllowWebsocket: true,
		HTTP2Support:   true,
		Meta:           npm.Meta{},
		Locations:      []any{},
	}

	if opts.SSLCertificateID > 0 {
		config.CertificateID = opts.SSLCertificateID
		config.SSLForced = true
		config.HSTSEnabled = true
	}

	if found {
		host, err := p.client.UpdateProxyHost(ctx, existing.ID, config)
		if err != nil {
			return "", fmt.Errorf("update proxy host: %w", err)
		}
		return strconv.Itoa(host.ID), nil
	}

	host, err := p.client.CreateProxyHost(ctx, config)
	if err != nil {
		return "", fmt.Errorf("create proxy host: %w", err)
	}
	return strconv.Itoa(host.ID), nil
}

func (p *NpmProvider) DeleteRoute(ctx context.Context, routeID string) error {
	if routeID == "" {
		return nil
	}

	id, err := strconv.Atoi(routeID)
	if err != nil {
		return fmt.Errorf("invalid npm proxy host id %q: %w", routeID, err)
	}

	if err := p.auth(ctx); err != nil {
		return err
	}

	return p.client.DeleteProxyHost(ctx, id)
}

func (p *NpmProvider) ContainerLabels(_ string, _ int) map[string]string {
	// NPM configures routing via its API, not Docker labels.
	return nil
}

func (p *NpmProvider) Ping(ctx context.Context) error {
	return p.client.Ping(ctx)
}

// auth authenticates to NPM if credentials are available.
func (p *NpmProvider) auth(ctx context.Context) error {
	if p.email == "" {
		return fmt.Errorf("NPM credentials not configured")
	}
	return p.client.Authenticate(ctx, p.email, p.password)
}
