package discovery

import (
	"context"
	"fmt"

	"github.com/pkg/errors"
	"github.com/sankofa/crossplane-provider-proxmox/pkg/proxmox"
)

// ProxmoxDiscoveryAgent discovers Proxmox resources
type ProxmoxDiscoveryAgent struct {
	client *proxmox.Client
	site   string
	region string
}

// NewProxmoxDiscoveryAgent creates a new Proxmox discovery agent
func NewProxmoxDiscoveryAgent(client *proxmox.Client, site, region string) *ProxmoxDiscoveryAgent {
	return &ProxmoxDiscoveryAgent{
		client: client,
		site:   site,
		region: region,
	}
}

// DiscoveredResource represents a discovered resource
type DiscoveredResource struct {
	ResourceType       string
	Provider           string
	ProviderID         string
	ProviderResourceID string
	Name               string
	Region             string
	SiteID             string
	Metadata           map[string]interface{}
	Tags               []string
}

// DiscoverVMs discovers all VMs in the Proxmox cluster
func (a *ProxmoxDiscoveryAgent) DiscoverVMs(ctx context.Context) ([]DiscoveredResource, error) {
	// Use Proxmox API to list all VMs
	// This requires implementing ListVMs in the Proxmox client
	// For now, we'll make a direct API call
	
	// Get cluster nodes first
	nodes, err := a.client.ListNodes(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to list nodes")
	}
	
	var resources []DiscoveredResource
	
	// List VMs from each node
	for _, node := range nodes {
		vms, err := a.client.ListVMs(ctx, node)
		if err != nil {
			// Log error but continue with other nodes
			continue
		}
		
		for _, vm := range vms {
			resources = append(resources, DiscoveredResource{
				ResourceType:       "VM",
				Provider:           "PROXMOX",
				ProviderID:         fmt.Sprintf("%d", vm.ID),
				ProviderResourceID: fmt.Sprintf("proxmox://nodes/%s/qemu/%d", node, vm.ID),
				Name:               vm.Name,
				Region:             a.region,
				SiteID:             a.site,
				Metadata: map[string]interface{}{
					"node":    node,
					"vmId":    vm.ID,
					"status":  vm.Status,
					"ip":      vm.IP,
					"created": vm.Created,
				},
				Tags: []string{fmt.Sprintf("node:%s", node)},
			})
		}
	}
	
	return resources, nil
}

// DiscoverStoragePools discovers all storage pools
func (a *ProxmoxDiscoveryAgent) DiscoverStoragePools(ctx context.Context) ([]DiscoveredResource, error) {
	// List storage pools from Proxmox API
	storages, err := a.client.ListStorages(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to list storage pools")
	}
	
	var resources []DiscoveredResource
	
	for _, storage := range storages {
		resources = append(resources, DiscoveredResource{
			ResourceType:       "STORAGE",
			Provider:           "PROXMOX",
			ProviderID:         storage.Name,
			ProviderResourceID: fmt.Sprintf("proxmox://storage/%s", storage.Name),
			Name:               storage.Name,
			Region:             a.region,
			SiteID:             a.site,
			Metadata: map[string]interface{}{
				"type":     storage.Type,
				"content":  storage.Content,
				"shared":   storage.Shared,
				"enabled":  storage.Enabled,
				"capacity": storage.Capacity,
				"used":     storage.Used,
			},
			Tags: []string{fmt.Sprintf("type:%s", storage.Type)},
		})
	}
	
	return resources, nil
}

// DiscoverNetworks discovers all network bridges
func (a *ProxmoxDiscoveryAgent) DiscoverNetworks(ctx context.Context) ([]DiscoveredResource, error) {
	// Get cluster nodes first
	nodes, err := a.client.ListNodes(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to list nodes")
	}
	
	var resources []DiscoveredResource
	seenNetworks := make(map[string]bool)
	
	// List networks from each node
	for _, node := range nodes {
		networks, err := a.client.ListNetworks(ctx, node)
		if err != nil {
			// Log error but continue with other nodes
			continue
		}
		
		for _, network := range networks {
			// Avoid duplicates (same network on multiple nodes)
			networkKey := fmt.Sprintf("%s-%s", network.Name, network.Type)
			if seenNetworks[networkKey] {
				continue
			}
			seenNetworks[networkKey] = true
			
			resources = append(resources, DiscoveredResource{
				ResourceType:       "NETWORK",
				Provider:           "PROXMOX",
				ProviderID:         network.Name,
				ProviderResourceID: fmt.Sprintf("proxmox://nodes/%s/network/%s", node, network.Name),
				Name:               network.Name,
				Region:             a.region,
				SiteID:             a.site,
				Metadata: map[string]interface{}{
					"node":     node,
					"type":     network.Type,
					"bridge":   network.Bridge,
					"vlan":     network.VLAN,
					"active":   network.Active,
					"autostart": network.Autostart,
				},
				Tags: []string{fmt.Sprintf("type:%s", network.Type)},
			})
		}
	}
	
	return resources, nil
}

// DiscoverClusters discovers Proxmox clusters
func (a *ProxmoxDiscoveryAgent) DiscoverClusters(ctx context.Context) ([]DiscoveredResource, error) {
	// Get cluster information
	clusterInfo, err := a.client.GetClusterInfo(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to get cluster info")
	}
	
	var resources []DiscoveredResource
	
	// Add cluster as a resource
	resources = append(resources, DiscoveredResource{
		ResourceType:       "CLUSTER",
		Provider:           "PROXMOX",
		ProviderID:         clusterInfo.Name,
		ProviderResourceID: fmt.Sprintf("proxmox://cluster/%s", clusterInfo.Name),
		Name:               clusterInfo.Name,
		Region:             a.region,
		SiteID:             a.site,
		Metadata: map[string]interface{}{
			"nodes":      clusterInfo.Nodes,
			"quorum":     clusterInfo.Quorum,
			"version":    clusterInfo.Version,
			"nodeCount":  len(clusterInfo.Nodes),
		},
		Tags: []string{"cluster"},
	})
	
	return resources, nil
}

// DiscoverAll discovers all Proxmox resources
func (a *ProxmoxDiscoveryAgent) DiscoverAll(ctx context.Context) ([]DiscoveredResource, error) {
	var allResources []DiscoveredResource

	// Discover VMs
	vms, err := a.DiscoverVMs(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover VMs")
	}
	allResources = append(allResources, vms...)

	// Discover storage pools
	storagePools, err := a.DiscoverStoragePools(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover storage pools")
	}
	allResources = append(allResources, storagePools...)

	// Discover networks
	networks, err := a.DiscoverNetworks(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover networks")
	}
	allResources = append(allResources, networks...)

	// Discover clusters
	clusters, err := a.DiscoverClusters(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover clusters")
	}
	allResources = append(allResources, clusters...)

	return allResources, nil
}

