package static

import (
	"context"
	"errors"
	"fmt"
	"log/slog"

	"github.com/alexei/tinyforge/internal/store"
	"github.com/alexei/tinyforge/internal/workload/plugin"
)

// teardown drops every artifact deploy created: the running container,
// the proxy route, the optional storage volume, and the container
// index row. Idempotent — a workload that never deployed is a no-op.
//
// Mirrors the legacy Manager.Remove + Stop combination: stop is
// implicit in RemoveContainer(force=true), and the volume removal
// happens only when storage was opted into (the named volume is
// otherwise nonexistent and best-effort delete would log a noisy
// warning).
func teardown(ctx context.Context, deps plugin.Deps, w plugin.Workload) error {
	cfg, err := plugin.SourceConfigOf[Config](w)
	if err != nil {
		return fmt.Errorf("static source: decode config: %w", err)
	}

	_, prevContainer, err := loadState(deps, w)
	if err != nil {
		return err
	}
	if prevContainer == nil {
		// Nothing was ever deployed — best-effort volume cleanup in
		// case storage was provisioned but the deploy crashed before
		// state landed, then return.
		if cfg.StorageEnabled {
			if err := deps.Docker.RemoveSiteVolume(ctx, siteVolumeKey(w)); err != nil {
				slog.Debug("static site: storage volume cleanup", "site", w.Name, "error", err)
			}
		}
		return nil
	}

	// Drop proxy route first so traffic stops landing on a container
	// that is about to disappear.
	if prevContainer.ProxyRouteID != "" {
		if err := deps.Proxy.DeleteRoute(ctx, prevContainer.ProxyRouteID); err != nil {
			slog.Warn("static site: failed to remove proxy route", "site", w.Name, "error", err)
		}
	}

	if prevContainer.ContainerID != "" {
		if err := deps.Docker.RemoveContainer(ctx, prevContainer.ContainerID, true); err != nil {
			slog.Warn("static site: failed to remove container", "site", w.Name, "error", err)
		}
	}

	if cfg.StorageEnabled {
		if err := deps.Docker.RemoveSiteVolume(ctx, siteVolumeKey(w)); err != nil {
			slog.Warn("static site: failed to remove storage volume", "site", w.Name, "error", err)
		}
	}

	// Delete the container row last so a partial failure leaves enough
	// state for a retry. ErrNotFound is fine.
	if err := deps.Store.DeleteContainer(prevContainer.ID); err != nil && !errors.Is(err, store.ErrNotFound) {
		slog.Warn("static site: failed to delete container row", "site", w.Name, "error", err)
	}
	// The per-workload save-mutex is reference-counted (see state.go) and
	// frees itself when the last holder releases, so teardown no longer
	// deletes it explicitly — doing so could race a concurrent saveState.
	return nil
}
