package sqlite

import (
	"context"
	"fmt"
	"strings"
)

type ManagedResource struct {
	ID             int64
	BatchID        int64
	HostID         int64
	ResourceType   string
	HostResourceID string
	ResourceName   string
}

type ManagedResourcesRepo struct {
	db execQuerier
}

func newManagedResourcesRepo(db execQuerier) *ManagedResourcesRepo {
	return &ManagedResourcesRepo{db: db}
}

func (r *ManagedResourcesRepo) Create(ctx context.Context, resource ManagedResource) (int64, error) {
	resourceType := strings.TrimSpace(resource.ResourceType)
	hostResourceID := strings.TrimSpace(resource.HostResourceID)
	resourceName := strings.TrimSpace(resource.ResourceName)

	switch {
	case resource.BatchID <= 0:
		return 0, fmt.Errorf("batch_id is required")
	case resource.HostID <= 0:
		return 0, fmt.Errorf("host_id is required")
	case resourceType == "":
		return 0, fmt.Errorf("resource_type is required")
	case hostResourceID == "":
		return 0, fmt.Errorf("host_resource_id is required")
	case resourceName == "":
		return 0, fmt.Errorf("resource_name is required")
	}

	result, err := r.db.ExecContext(ctx, `INSERT INTO managed_resources (batch_id, host_id, resource_type, host_resource_id, resource_name) VALUES (?, ?, ?, ?, ?)`, resource.BatchID, resource.HostID, resourceType, hostResourceID, resourceName)
	if err != nil {
		return 0, fmt.Errorf("insert managed resource %q: %w", hostResourceID, err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("read inserted managed resource id for %q: %w", hostResourceID, err)
	}
	return id, nil
}

func (r *ManagedResourcesRepo) GetByResourceIdentity(ctx context.Context, hostID int64, resourceType, hostResourceID string) (ManagedResource, error) {
	resourceType = strings.TrimSpace(resourceType)
	hostResourceID = strings.TrimSpace(hostResourceID)
	if hostID <= 0 {
		return ManagedResource{}, fmt.Errorf("host_id is required")
	}
	if resourceType == "" {
		return ManagedResource{}, fmt.Errorf("resource_type is required")
	}
	if hostResourceID == "" {
		return ManagedResource{}, fmt.Errorf("host_resource_id is required")
	}

	var resource ManagedResource
	if err := r.db.QueryRowContext(ctx, `SELECT id, batch_id, host_id, resource_type, host_resource_id, resource_name FROM managed_resources WHERE host_id = ? AND resource_type = ? AND host_resource_id = ?`, hostID, resourceType, hostResourceID).Scan(&resource.ID, &resource.BatchID, &resource.HostID, &resource.ResourceType, &resource.HostResourceID, &resource.ResourceName); err != nil {
		return ManagedResource{}, err
	}
	return resource, nil
}

func (r *ManagedResourcesRepo) GetByBatchID(ctx context.Context, batchID int64) ([]ManagedResource, error) {
	if batchID <= 0 {
		return nil, fmt.Errorf("batch_id is required")
	}

	rows, err := r.db.QueryContext(ctx, `SELECT id, batch_id, host_id, resource_type, host_resource_id, resource_name FROM managed_resources WHERE batch_id = ? ORDER BY id`, batchID)
	if err != nil {
		return nil, fmt.Errorf("query managed resources: %w", err)
	}
	defer rows.Close()

	resources := make([]ManagedResource, 0)
	for rows.Next() {
		var resource ManagedResource
		if err := rows.Scan(&resource.ID, &resource.BatchID, &resource.HostID, &resource.ResourceType, &resource.HostResourceID, &resource.ResourceName); err != nil {
			return nil, fmt.Errorf("scan managed resource: %w", err)
		}
		resources = append(resources, resource)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate managed resources: %w", err)
	}
	return resources, nil
}

func (r *ManagedResourcesRepo) ListByProviderID(ctx context.Context, providerID int64) ([]ManagedResource, error) {
	if providerID <= 0 {
		return nil, fmt.Errorf("provider_id is required")
	}

	rows, err := r.db.QueryContext(ctx, `SELECT mr.id, mr.batch_id, mr.host_id, mr.resource_type, mr.host_resource_id, mr.resource_name
		FROM managed_resources mr
		JOIN import_batches ib ON ib.id = mr.batch_id
		WHERE ib.provider_id = ?
		ORDER BY mr.id`, providerID)
	if err != nil {
		return nil, fmt.Errorf("query managed resources by provider_id %d: %w", providerID, err)
	}
	defer rows.Close()

	resources := make([]ManagedResource, 0)
	for rows.Next() {
		var resource ManagedResource
		if err := rows.Scan(&resource.ID, &resource.BatchID, &resource.HostID, &resource.ResourceType, &resource.HostResourceID, &resource.ResourceName); err != nil {
			return nil, fmt.Errorf("scan managed resource by provider_id %d: %w", providerID, err)
		}
		resources = append(resources, resource)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate managed resources by provider_id %d: %w", providerID, err)
	}
	return resources, nil
}

func (r *ManagedResourcesRepo) ListByProviderIDAndHostID(ctx context.Context, providerID, hostID int64) ([]ManagedResource, error) {
	if providerID <= 0 {
		return nil, fmt.Errorf("provider_id is required")
	}
	if hostID <= 0 {
		return nil, fmt.Errorf("host_id is required")
	}

	rows, err := r.db.QueryContext(ctx, `SELECT mr.id, mr.batch_id, mr.host_id, mr.resource_type, mr.host_resource_id, mr.resource_name
		FROM managed_resources mr
		JOIN import_batches ib ON ib.id = mr.batch_id
		WHERE ib.provider_id = ? AND mr.host_id = ?
		ORDER BY mr.id`, providerID, hostID)
	if err != nil {
		return nil, fmt.Errorf("query managed resources by provider_id %d and host_id %d: %w", providerID, hostID, err)
	}
	defer rows.Close()

	resources := make([]ManagedResource, 0)
	for rows.Next() {
		var resource ManagedResource
		if err := rows.Scan(&resource.ID, &resource.BatchID, &resource.HostID, &resource.ResourceType, &resource.HostResourceID, &resource.ResourceName); err != nil {
			return nil, fmt.Errorf("scan managed resource by provider_id %d and host_id %d: %w", providerID, hostID, err)
		}
		resources = append(resources, resource)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate managed resources by provider_id %d and host_id %d: %w", providerID, hostID, err)
	}
	return resources, nil
}
