package agent

import (
	"context"
	"fmt"

	mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
	"git.wntrmute.dev/mc/mcp/internal/registry"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// UndeployService fully tears down a service: removes routes, DNS records,
// TLS certificates, stops and removes containers, releases ports, and marks
// the service inactive. This is the inverse of Deploy.
func (a *Agent) UndeployService(ctx context.Context, req *mcpv1.UndeployServiceRequest) (*mcpv1.UndeployServiceResponse, error) {
	a.Logger.Info("UndeployService", "service", req.GetName())

	if req.GetName() == "" {
		return nil, status.Error(codes.InvalidArgument, "service name is required")
	}

	serviceName := req.GetName()

	components, err := registry.ListComponents(a.DB, serviceName)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "list components: %v", err)
	}

	var results []*mcpv1.ComponentResult
	dnsRemoved := false

	for _, c := range components {
		r := a.undeployComponent(ctx, serviceName, &c, &dnsRemoved)
		results = append(results, r)
	}

	// Mark the service as inactive.
	if err := registry.UpdateServiceActive(a.DB, serviceName, false); err != nil {
		a.Logger.Warn("failed to mark service inactive", "service", serviceName, "err", err)
	}

	return &mcpv1.UndeployServiceResponse{Results: results}, nil
}

// undeployComponent tears down a single component. The dnsRemoved flag
// tracks whether DNS has already been removed for this service (DNS is
// per-service, not per-component).
func (a *Agent) undeployComponent(ctx context.Context, serviceName string, c *registry.Component, dnsRemoved *bool) *mcpv1.ComponentResult {
	containerName := ContainerNameFor(serviceName, c.Name)
	r := &mcpv1.ComponentResult{Name: c.Name, Success: true}

	// 1. Remove mc-proxy routes.
	if len(c.Routes) > 0 && a.Proxy != nil {
		if err := a.Proxy.RemoveRoutes(ctx, serviceName, c.Routes); err != nil {
			a.Logger.Warn("failed to remove routes", "service", serviceName, "component", c.Name, "err", err)
		}
	}

	// 2. Remove DNS records (once per service).
	if len(c.Routes) > 0 && a.DNS != nil && !*dnsRemoved {
		if err := a.DNS.RemoveRecord(ctx, serviceName); err != nil {
			a.Logger.Warn("failed to remove DNS record", "service", serviceName, "err", err)
		}
		*dnsRemoved = true
	}

	// 3. Remove TLS certs (L7 routes only).
	if hasL7Routes(c.Routes) && a.Certs != nil {
		if err := a.Certs.RemoveCert(serviceName); err != nil {
			a.Logger.Warn("failed to remove TLS cert", "service", serviceName, "err", err)
		}
	}

	// 4. Stop and remove the container.
	if err := a.Runtime.Stop(ctx, containerName); err != nil {
		a.Logger.Info("stop container (ignored)", "container", containerName, "error", err)
	}
	if err := a.Runtime.Remove(ctx, containerName); err != nil {
		a.Logger.Info("remove container (ignored)", "container", containerName, "error", err)
	}

	// 5. Release allocated ports.
	if a.PortAlloc != nil {
		hostPorts, err := registry.GetRouteHostPorts(a.DB, serviceName, c.Name)
		if err == nil {
			for _, port := range hostPorts {
				a.PortAlloc.Release(port)
			}
		}
	}

	// 6. Update registry state.
	if err := registry.UpdateComponentState(a.DB, serviceName, c.Name, "removed", "removed"); err != nil {
		r.Success = false
		r.Error = fmt.Sprintf("update state: %v", err)
	}

	return r
}
