package service

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

	"github.com/shankar0123/certctl/internal/domain"
	"github.com/shankar0123/certctl/internal/repository"
)

// TargetService provides business logic for deployment target management.
type TargetService struct {
	targetRepo   repository.TargetRepository
	auditService *AuditService
}

// NewTargetService creates a new target service.
func NewTargetService(
	targetRepo repository.TargetRepository,
	auditService *AuditService,
) *TargetService {
	return &TargetService{
		targetRepo:   targetRepo,
		auditService: auditService,
	}
}

// List returns a paginated list of deployment targets.
func (s *TargetService) List(ctx context.Context, page, perPage int) ([]*domain.DeploymentTarget, int64, error) {
	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 50
	}

	targets, err := s.targetRepo.List(ctx)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to list targets: %w", err)
	}
	total := int64(len(targets))
	start := (page - 1) * perPage
	if start >= int(total) {
		return nil, total, nil
	}
	end := start + perPage
	if end > int(total) {
		end = int(total)
	}
	return targets[start:end], total, nil
}

// Get retrieves a deployment target by ID.
func (s *TargetService) Get(ctx context.Context, id string) (*domain.DeploymentTarget, error) {
	target, err := s.targetRepo.Get(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("failed to get target %s: %w", id, err)
	}
	return target, nil
}

// Create validates and stores a new deployment target.
func (s *TargetService) Create(ctx context.Context, target *domain.DeploymentTarget, actor string) error {
	if target.Name == "" {
		return fmt.Errorf("target name is required")
	}

	if target.ID == "" {
		target.ID = generateID("target")
	}
	now := time.Now()
	if target.CreatedAt.IsZero() {
		target.CreatedAt = now
	}
	if target.UpdatedAt.IsZero() {
		target.UpdatedAt = now
	}
	if err := s.targetRepo.Create(ctx, target); err != nil {
		return fmt.Errorf("failed to create target: %w", err)
	}

	if s.auditService != nil {
		if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_target", "target", target.ID, nil); auditErr != nil {
			slog.Error("failed to record audit event", "error", auditErr)
		}
	}

	return nil
}

// Update modifies an existing deployment target.
func (s *TargetService) Update(ctx context.Context, id string, target *domain.DeploymentTarget, actor string) error {
	if target.Name == "" {
		return fmt.Errorf("target name is required")
	}

	target.ID = id
	if err := s.targetRepo.Update(ctx, target); err != nil {
		return fmt.Errorf("failed to update target %s: %w", id, err)
	}

	if s.auditService != nil {
		if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_target", "target", id, nil); auditErr != nil {
			slog.Error("failed to record audit event", "error", auditErr)
		}
	}

	return nil
}

// Delete removes a deployment target.
func (s *TargetService) Delete(ctx context.Context, id string, actor string) error {
	if err := s.targetRepo.Delete(ctx, id); err != nil {
		return fmt.Errorf("failed to delete target %s: %w", id, err)
	}

	if s.auditService != nil {
		if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_target", "target", id, nil); auditErr != nil {
			slog.Error("failed to record audit event", "error", auditErr)
		}
	}

	return nil
}

// ListTargets returns paginated targets (handler interface method).
func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) {
	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 50
	}

	targets, err := s.targetRepo.List(context.Background())
	if err != nil {
		return nil, 0, fmt.Errorf("failed to list targets: %w", err)
	}
	total := int64(len(targets))

	var result []domain.DeploymentTarget
	for _, t := range targets {
		if t != nil {
			result = append(result, *t)
		}
	}

	return result, total, nil
}

// GetTarget returns a single target (handler interface method).
func (s *TargetService) GetTarget(id string) (*domain.DeploymentTarget, error) {
	return s.targetRepo.Get(context.Background(), id)
}

// CreateTarget creates a new target (handler interface method).
func (s *TargetService) CreateTarget(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) {
	if target.ID == "" {
		target.ID = generateID("target")
	}
	now := time.Now()
	if target.CreatedAt.IsZero() {
		target.CreatedAt = now
	}
	if target.UpdatedAt.IsZero() {
		target.UpdatedAt = now
	}
	if err := s.targetRepo.Create(context.Background(), &target); err != nil {
		return nil, fmt.Errorf("failed to create target: %w", err)
	}
	return &target, nil
}

// UpdateTarget modifies a target (handler interface method).
func (s *TargetService) UpdateTarget(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) {
	target.ID = id
	if err := s.targetRepo.Update(context.Background(), &target); err != nil {
		return nil, fmt.Errorf("failed to update target: %w", err)
	}
	return &target, nil
}

// DeleteTarget removes a target (handler interface method).
func (s *TargetService) DeleteTarget(id string) error {
	return s.targetRepo.Delete(context.Background(), id)
}
