// Package service provides business logic / use cases for the application.
// This file contains shell and git command execution functionality.
package service

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

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

// ExecuteShellRequest contains parameters for running a shell command.
type ExecuteShellRequest struct {
	ProjectID domain.ProjectID
	Command   string
	StreamID  string
	Audit     *AuditContext // Optional audit context
}

// ExecuteShellResult contains the result of queuing a shell command.
type ExecuteShellResult struct {
	CommandID domain.CommandID
	StreamURL string
}

// ExecuteShell runs a shell command in the project's pod.
func (s *ProjectService) ExecuteShell(ctx context.Context, req ExecuteShellRequest) (*ExecuteShellResult, error) {
	// Validate project exists
	project, err := s.projects.Get(ctx, req.ProjectID)
	if err != nil {
		return nil, err
	}

	// Validate command
	if req.Command == "" {
		return nil, fmt.Errorf("%w: command is required", domain.ErrInvalidCommand)
	}
	if err := sanitize.ShellCommand(req.Command); err != nil {
		return nil, fmt.Errorf("%w: %w", domain.ErrCommandSanitization, err)
	}

	// Validate stream ID
	if err := sanitize.StreamID(req.StreamID); err != nil {
		return nil, fmt.Errorf("%w: %w", domain.ErrInvalidCommand, err)
	}

	// Generate command ID
	cmdNum := s.cmdID.Add(1)
	cmdID := domain.CommandID(fmt.Sprintf("cmd-%s-%03d", req.ProjectID, cmdNum))
	if req.StreamID != "" {
		cmdID = domain.CommandID(req.StreamID)
	}

	// Create command
	cmd := &domain.Command{
		ID:        cmdID,
		ProjectID: req.ProjectID,
		Type:      domain.CommandTypeShell,
		Args:      []string{req.Command},
		StartedAt: time.Now(),
	}

	// Log audit start if audit logger is configured
	if s.auditLogger != nil && req.Audit != nil {
		log := logging.FromContext(ctx).WithService("ProjectService")
		argsJSON, _ := json.Marshal(cmd.Args)
		auditEntry := &domain.AuditLogEntry{
			ID:          uuid.New().String(),
			APIKeyID:    req.Audit.APIKeyID,
			CommandID:   string(cmdID),
			ProjectID:   string(req.ProjectID),
			CommandType: domain.CommandTypeShell,
			Args:        string(argsJSON),
			ClientIP:    req.Audit.ClientIP,
			UserAgent:   req.Audit.UserAgent,
			StartedAt:   cmd.StartedAt,
			Status:      domain.AuditStatusRunning,
		}
		if err := s.auditLogger.LogCommandStart(ctx, auditEntry); err != nil {
			log.Warn("failed to log audit start", "command_id", cmdID, logging.FieldError, err)
		}
	}

	// Execute in background
	go s.executeCommand(ctx, project.PodName, cmd)

	return &ExecuteShellResult{
		CommandID: cmdID,
		StreamURL: fmt.Sprintf("/projects/%s/events?stream_id=%s", req.ProjectID, cmdID),
	}, nil
}

// ExecuteGitRequest contains parameters for running a git command.
type ExecuteGitRequest struct {
	ProjectID domain.ProjectID
	Args      []string
	StreamID  string
	Audit     *AuditContext // Optional audit context
}

// ExecuteGitResult contains the result of queuing a git command.
type ExecuteGitResult struct {
	CommandID domain.CommandID
	StreamURL string
}

// ExecuteGit runs a git command in the project's pod.
func (s *ProjectService) ExecuteGit(ctx context.Context, req ExecuteGitRequest) (*ExecuteGitResult, error) {
	// Validate project exists
	project, err := s.projects.Get(ctx, req.ProjectID)
	if err != nil {
		return nil, err
	}

	// Validate args
	if len(req.Args) == 0 {
		return nil, fmt.Errorf("%w: args is required", domain.ErrInvalidCommand)
	}
	if err := sanitize.GitArgs(req.Args); err != nil {
		return nil, fmt.Errorf("%w: %w", domain.ErrCommandSanitization, err)
	}

	// Validate stream ID
	if err := sanitize.StreamID(req.StreamID); err != nil {
		return nil, fmt.Errorf("%w: %w", domain.ErrInvalidCommand, err)
	}

	// Generate command ID
	cmdNum := s.cmdID.Add(1)
	cmdID := domain.CommandID(fmt.Sprintf("cmd-%s-%03d", req.ProjectID, cmdNum))
	if req.StreamID != "" {
		cmdID = domain.CommandID(req.StreamID)
	}

	// Create command
	cmd := &domain.Command{
		ID:        cmdID,
		ProjectID: req.ProjectID,
		Type:      domain.CommandTypeGit,
		Args:      req.Args,
		StartedAt: time.Now(),
	}

	// Log audit start if audit logger is configured
	if s.auditLogger != nil && req.Audit != nil {
		log := logging.FromContext(ctx).WithService("ProjectService")
		argsJSON, _ := json.Marshal(cmd.Args)
		auditEntry := &domain.AuditLogEntry{
			ID:          uuid.New().String(),
			APIKeyID:    req.Audit.APIKeyID,
			CommandID:   string(cmdID),
			ProjectID:   string(req.ProjectID),
			CommandType: domain.CommandTypeGit,
			Args:        string(argsJSON),
			ClientIP:    req.Audit.ClientIP,
			UserAgent:   req.Audit.UserAgent,
			StartedAt:   cmd.StartedAt,
			Status:      domain.AuditStatusRunning,
		}
		if err := s.auditLogger.LogCommandStart(ctx, auditEntry); err != nil {
			log.Warn("failed to log audit start", "command_id", cmdID, logging.FieldError, err)
		}
	}

	// Execute in background
	go s.executeCommand(ctx, project.PodName, cmd)

	return &ExecuteGitResult{
		CommandID: cmdID,
		StreamURL: fmt.Sprintf("/projects/%s/events?stream_id=%s", req.ProjectID, cmdID),
	}, nil
}
