package service

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/logging"
	"github.com/orchard9/rdev/internal/port"
)

// BlueprintService orchestrates blueprint operations.
type BlueprintService struct {
	repo port.BlueprintRepository
}

// NewBlueprintService creates a new blueprint service.
func NewBlueprintService(repo port.BlueprintRepository) *BlueprintService {
	return &BlueprintService{repo: repo}
}

// CreateBlueprint creates a new blueprint.
func (s *BlueprintService) CreateBlueprint(ctx context.Context, projectID, name, description string, spec map[string]any) (*domain.Blueprint, error) {
	if projectID == "" {
		return nil, fmt.Errorf("project_id is required")
	}
	if name == "" {
		return nil, fmt.Errorf("name is required")
	}
	if spec == nil {
		spec = make(map[string]any)
	}

	blueprint := &domain.Blueprint{
		ID:          domain.BlueprintID(uuid.New().String()),
		ProjectID:   projectID,
		Name:        name,
		Description: description,
		Spec:        spec,
	}

	startTime := time.Now()
	if err := s.repo.CreateBlueprint(ctx, blueprint); err != nil {
		return nil, err
	}

	log := logging.FromContext(ctx)
	log.Info("blueprint created",
		"blueprint_id", blueprint.ID,
		logging.FieldProjectID, projectID,
		logging.FieldOperation, "create_blueprint",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
		"name", name,
		"has_spec", len(spec) > 0,
	)

	return blueprint, nil
}

// GetBlueprint retrieves a blueprint by ID.
func (s *BlueprintService) GetBlueprint(ctx context.Context, id domain.BlueprintID) (*domain.Blueprint, error) {
	return s.repo.GetBlueprint(ctx, id)
}

// ListBlueprints returns all blueprints for a project.
func (s *BlueprintService) ListBlueprints(ctx context.Context, projectID string) ([]*domain.Blueprint, error) {
	return s.repo.ListBlueprints(ctx, projectID)
}

// UpdateBlueprint updates a blueprint's metadata and spec.
func (s *BlueprintService) UpdateBlueprint(ctx context.Context, id domain.BlueprintID, name, description string, spec map[string]any) error {
	// Get existing blueprint first
	blueprint, err := s.repo.GetBlueprint(ctx, id)
	if err != nil {
		return err
	}

	// Update fields
	if name != "" {
		blueprint.Name = name
	}
	blueprint.Description = description
	if spec != nil {
		blueprint.Spec = spec
	}

	startTime := time.Now()
	if err := s.repo.UpdateBlueprint(ctx, blueprint); err != nil {
		return err
	}

	log := logging.FromContext(ctx)
	log.Info("blueprint updated",
		"blueprint_id", id,
		logging.FieldProjectID, blueprint.ProjectID,
		logging.FieldOperation, "update_blueprint",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
	)

	return nil
}

// DeleteBlueprint deletes a blueprint.
func (s *BlueprintService) DeleteBlueprint(ctx context.Context, id domain.BlueprintID) error {
	startTime := time.Now()
	if err := s.repo.DeleteBlueprint(ctx, id); err != nil {
		return err
	}

	log := logging.FromContext(ctx)
	log.Info("blueprint deleted",
		"blueprint_id", id,
		logging.FieldOperation, "delete_blueprint",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
	)
	return nil
}
