package action

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"time"
)

// NotifyStatusInput is input for NotifyStatusActivity
type NotifyStatusInput struct {
	Channel string `json:"channel"` // slack, email, webhook
	Status  string `json:"status"`  // success, failure, warning
	Message string `json:"message"`
}

// NotifyStatusOutput is output from NotifyStatusActivity
type NotifyStatusOutput struct {
	NotificationID string `json:"notificationId"`
	Timestamp      string `json:"timestamp"`
}

// NotifyStatusActivity sends notifications
func NotifyStatusActivity(ctx context.Context, in NotifyStatusInput) (NotifyStatusOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("NotifyStatusActivity started", "channel", in.Channel, "status", in.Status)

	timestamp := time.Now().Format(time.RFC3339)
	notificationID := fmt.Sprintf("notify-%d", time.Now().UnixNano())

	switch in.Channel {
	case "slack":
		if err := sendSlackNotification(ctx, in); err != nil {
			logger.Warn("Slack notification failed", "error", err)
			// Don't fail the activity, just log
		}
	case "webhook":
		if err := sendWebhookNotification(ctx, in); err != nil {
			logger.Warn("Webhook notification failed", "error", err)
		}
	case "email":
		// Email would require SMTP setup - log for now
		logger.Info("Email notification (logged)", "message", in.Message)
	default:
		logger.Info("Notification logged", "channel", in.Channel, "message", in.Message)
	}

	logger.Info("NotifyStatusActivity completed", "notificationId", notificationID)
	return NotifyStatusOutput{
		NotificationID: notificationID,
		Timestamp:      timestamp,
	}, nil
}

func sendSlackNotification(ctx context.Context, in NotifyStatusInput) error {
	webhookURL := os.Getenv("SLACK_WEBHOOK_URL")
	if webhookURL == "" {
		return fmt.Errorf("SLACK_WEBHOOK_URL not set")
	}

	// Map status to emoji
	emoji := "ℹ️"
	switch in.Status {
	case "success":
		emoji = "✅"
	case "failure":
		emoji = "❌"
	case "warning":
		emoji = "⚠️"
	}

	payload := map[string]string{
		"text": fmt.Sprintf("%s *%s*: %s", emoji, in.Status, in.Message),
	}

	body, _ := json.Marshal(payload)
	req, err := http.NewRequestWithContext(ctx, "POST", webhookURL, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("slack returned status %d", resp.StatusCode)
	}

	return nil
}

func sendWebhookNotification(ctx context.Context, in NotifyStatusInput) error {
	webhookURL := os.Getenv("NOTIFICATION_WEBHOOK_URL")
	if webhookURL == "" {
		return fmt.Errorf("NOTIFICATION_WEBHOOK_URL not set")
	}

	payload := map[string]interface{}{
		"channel":   in.Channel,
		"status":    in.Status,
		"message":   in.Message,
		"timestamp": time.Now().Format(time.RFC3339),
	}

	body, _ := json.Marshal(payload)
	req, err := http.NewRequestWithContext(ctx, "POST", webhookURL, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return fmt.Errorf("webhook returned status %d", resp.StatusCode)
	}

	return nil
}

// ArchiveResultsInput is input for ArchiveResultsActivity
type ArchiveResultsInput struct {
	ReportPath  string                 `json:"reportPath"`
	Destination string                 `json:"destination"` // s3://bucket/path or local path
	Metadata    map[string]interface{} `json:"metadata,omitempty"`
}

// ArchiveResultsOutput is output from ArchiveResultsActivity
type ArchiveResultsOutput struct {
	ArchiveURL  string `json:"archiveUrl"`
	ArchiveSize int64  `json:"archiveSize"`
}

// ArchiveResultsActivity archives results to storage
func ArchiveResultsActivity(ctx context.Context, in ArchiveResultsInput) (ArchiveResultsOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("ArchiveResultsActivity started", "reportPath", in.ReportPath, "destination", in.Destination)

	// Check if source exists
	info, err := os.Stat(in.ReportPath)
	if err != nil {
		return ArchiveResultsOutput{}, fmt.Errorf("report not found: %s", in.ReportPath)
	}

	// For now, just copy to local destination or log for cloud
	var archiveURL string
	var archiveSize int64

	if len(in.Destination) > 5 && in.Destination[:5] == "s3://" {
		// Would use AWS SDK - for now just log
		logger.Info("Would upload to S3", "destination", in.Destination)
		archiveURL = in.Destination
		archiveSize = info.Size()
	} else {
		// Copy to local destination
		content, err := os.ReadFile(in.ReportPath)
		if err != nil {
			return ArchiveResultsOutput{}, fmt.Errorf("failed to read report: %w", err)
		}

		destPath := in.Destination
		if destPath == "" {
			destPath = fmt.Sprintf("/tmp/archive-%d", time.Now().UnixNano())
		}

		if err := os.WriteFile(destPath, content, 0644); err != nil {
			return ArchiveResultsOutput{}, fmt.Errorf("failed to write archive: %w", err)
		}

		archiveURL = destPath
		archiveSize = int64(len(content))
	}

	logger.Info("ArchiveResultsActivity completed", "archiveUrl", archiveURL, "size", archiveSize)
	return ArchiveResultsOutput{
		ArchiveURL:  archiveURL,
		ArchiveSize: archiveSize,
	}, nil
}

// DeploymentPreCheckInput is input for DeploymentPreCheckActivity
type DeploymentPreCheckInput struct {
	Path      string `json:"path"`
	CheckType string `json:"checkType,omitempty"` // lint, test, build, all
}

// DeploymentPreCheckOutput is output from DeploymentPreCheckActivity
type DeploymentPreCheckOutput struct {
	Passed   bool     `json:"passed"`
	Failures []string `json:"failures"`
	Warnings []string `json:"warnings"`
}

// DeploymentPreCheckActivity validates deployment readiness
func DeploymentPreCheckActivity(ctx context.Context, in DeploymentPreCheckInput) (DeploymentPreCheckOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("DeploymentPreCheckActivity started", "path", in.Path, "checkType", in.CheckType)

	output := DeploymentPreCheckOutput{
		Passed:   true,
		Failures: []string{},
		Warnings: []string{},
	}

	checkType := in.CheckType
	if checkType == "" {
		checkType = "all"
	}

	// Check if path exists
	if _, err := os.Stat(in.Path); os.IsNotExist(err) {
		output.Passed = false
		output.Failures = append(output.Failures, fmt.Sprintf("path does not exist: %s", in.Path))
		return output, nil
	}

	// Check for Go project
	isGo := false
	if _, err := os.Stat(fmt.Sprintf("%s/go.mod", in.Path)); err == nil {
		isGo = true
	}

	if isGo && (checkType == "all" || checkType == "build") {
		// Try go build
		cmd := exec.CommandContext(ctx, "go", "build", "./...")
		cmd.Dir = in.Path
		if buildOut, err := cmd.CombinedOutput(); err != nil {
			output.Passed = false
			output.Failures = append(output.Failures, fmt.Sprintf("build failed: %s", string(buildOut)))
		}
	}

	if isGo && (checkType == "all" || checkType == "test") {
		// Try go test
		cmd := exec.CommandContext(ctx, "go", "test", "-short", "./...")
		cmd.Dir = in.Path
		if testOut, err := cmd.CombinedOutput(); err != nil {
			output.Passed = false
			output.Failures = append(output.Failures, fmt.Sprintf("tests failed: %s", string(testOut)))
		}
	}

	if isGo && (checkType == "all" || checkType == "lint") {
		// Try go vet
		cmd := exec.CommandContext(ctx, "go", "vet", "./...")
		cmd.Dir = in.Path
		if vetOut, err := cmd.CombinedOutput(); err != nil {
			output.Warnings = append(output.Warnings, fmt.Sprintf("vet issues: %s", string(vetOut)))
		}
	}

	logger.Info("DeploymentPreCheckActivity completed", "passed", output.Passed, "failures", len(output.Failures))
	return output, nil
}

// ApproveWorkflowInput is input for ApproveWorkflowActivity
type ApproveWorkflowInput struct {
	WorkflowID        string `json:"workflowId"`
	RequiredApprovals int    `json:"requiredApprovals,omitempty"`
	TimeoutMinutes    int    `json:"timeoutMinutes,omitempty"`
}

// ApproveWorkflowOutput is output from ApproveWorkflowActivity
type ApproveWorkflowOutput struct {
	Approved  bool   `json:"approved"`
	Approver  string `json:"approver,omitempty"`
	Timestamp string `json:"timestamp"`
}

// ApproveWorkflowActivity handles approval workflow (auto-approves for now)
func ApproveWorkflowActivity(ctx context.Context, in ApproveWorkflowInput) (ApproveWorkflowOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("ApproveWorkflowActivity started", "workflowId", in.WorkflowID)

	// For now, auto-approve
	// In production, this would wait for human approval via signal or external system

	timestamp := time.Now().Format(time.RFC3339)

	logger.Info("ApproveWorkflowActivity completed (auto-approved)")
	return ApproveWorkflowOutput{
		Approved:  true,
		Approver:  "system-auto",
		Timestamp: timestamp,
	}, nil
}
