package main

import (
	"context"
	"fmt"
	"io"
	"log/slog"
	"os"
	"strings"

	"github.com/nomadcode/nomadcode-core/internal/adapters/plane"
)

type EnvConfig struct {
	BaseURL       string
	Token         string
	WorkspaceSlug string
	ProjectID     string
	WorkItemID    string
	SmokeApply    bool
	SmokeComment  string
	SmokeStateID  string
}

func LoadConfig(getenv func(string) string) (EnvConfig, error) {
	var cfg EnvConfig
	cfg.BaseURL = strings.TrimSpace(getenv("PLANE_BASE_URL"))
	cfg.Token = strings.TrimSpace(getenv("PLANE_TOKEN"))
	cfg.WorkspaceSlug = strings.TrimSpace(getenv("PLANE_WORKSPACE_SLUG"))
	cfg.ProjectID = strings.TrimSpace(getenv("PLANE_PROJECT_ID"))
	cfg.WorkItemID = strings.TrimSpace(getenv("PLANE_WORK_ITEM_ID"))

	var missing []string
	if cfg.BaseURL == "" {
		missing = append(missing, "PLANE_BASE_URL")
	}
	if cfg.Token == "" {
		missing = append(missing, "PLANE_TOKEN")
	}
	if cfg.WorkspaceSlug == "" {
		missing = append(missing, "PLANE_WORKSPACE_SLUG")
	}
	if cfg.ProjectID == "" {
		missing = append(missing, "PLANE_PROJECT_ID")
	}
	if cfg.WorkItemID == "" {
		missing = append(missing, "PLANE_WORK_ITEM_ID")
	}

	if len(missing) > 0 {
		return cfg, fmt.Errorf("missing required environment variables: %s", strings.Join(missing, ", "))
	}

	applyStr := getenv("PLANE_SMOKE_APPLY")
	cfg.SmokeApply = applyStr == "1" || strings.ToLower(applyStr) == "true"

	if cfg.SmokeApply {
		cfg.SmokeComment = strings.TrimSpace(getenv("PLANE_SMOKE_COMMENT"))
		cfg.SmokeStateID = strings.TrimSpace(getenv("PLANE_SMOKE_STATE_ID"))

		var missingSmoke []string
		if cfg.SmokeComment == "" {
			missingSmoke = append(missingSmoke, "PLANE_SMOKE_COMMENT")
		}
		if cfg.SmokeStateID == "" {
			missingSmoke = append(missingSmoke, "PLANE_SMOKE_STATE_ID")
		}
		if len(missingSmoke) > 0 {
			return cfg, fmt.Errorf("PLANE_SMOKE_APPLY is enabled but missing required smoke variables: %s", strings.Join(missingSmoke, ", "))
		}
	}

	return cfg, nil
}

type PlaneClient interface {
	GetWorkItem(ctx context.Context, ref plane.WorkItemRef) (plane.WorkItem, error)
	AddComment(ctx context.Context, input plane.AddCommentInput) error
	UpdateIssueStatus(ctx context.Context, input plane.UpdateIssueStatusInput) error
}

func RunSmoke(ctx context.Context, cfg EnvConfig, client PlaneClient, out io.Writer) error {
	ref := plane.WorkItemRef{
		WorkspaceSlug: cfg.WorkspaceSlug,
		ProjectID:     cfg.ProjectID,
		WorkItemID:    cfg.WorkItemID,
	}

	item, err := client.GetWorkItem(ctx, ref)
	if err != nil {
		return fmt.Errorf("failed to lookup work item: %w", err)
	}

	fmt.Fprintf(out, "lookup ok: id=%s name=%s\n", item.ID, item.Name)

	if !cfg.SmokeApply {
		fmt.Fprintln(out, "write smoke skipped: set PLANE_SMOKE_APPLY=1")
		return nil
	}

	err = client.AddComment(ctx, plane.AddCommentInput{
		Ref:            ref,
		CommentHTML:    fmt.Sprintf("<p>%s</p>", cfg.SmokeComment),
		ExternalSource: "nomadcode",
		ExternalID:     "plane-smoke-test",
	})
	if err != nil {
		return fmt.Errorf("failed to add comment: %w", err)
	}
	fmt.Fprintln(out, "comment ok")

	err = client.UpdateIssueStatus(ctx, plane.UpdateIssueStatusInput{
		Ref:    ref,
		Status: cfg.SmokeStateID,
	})
	if err != nil {
		return fmt.Errorf("failed to update state: %w", err)
	}
	fmt.Fprintf(out, "state update ok: state_id=%s\n", cfg.SmokeStateID)

	return nil
}

func main() {
	cfg, err := LoadConfig(os.Getenv)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
		fmt.Fprintln(os.Stderr, "\nUsage:")
		fmt.Fprintln(os.Stderr, "  Required environment variables:")
		fmt.Fprintln(os.Stderr, "    PLANE_BASE_URL          - Base URL of Plane instance")
		fmt.Fprintln(os.Stderr, "    PLANE_TOKEN             - API Token (will be redacted in logs)")
		fmt.Fprintln(os.Stderr, "    PLANE_WORKSPACE_SLUG    - Workspace slug (e.g. general)")
		fmt.Fprintln(os.Stderr, "    PLANE_PROJECT_ID        - Project UUID")
		fmt.Fprintln(os.Stderr, "    PLANE_WORK_ITEM_ID      - Work Item UUID")
		fmt.Fprintln(os.Stderr, "  Optional variables for write operations:")
		fmt.Fprintln(os.Stderr, "    PLANE_SMOKE_APPLY       - Set to 1 to enable write smoke tests")
		fmt.Fprintln(os.Stderr, "    PLANE_SMOKE_COMMENT     - HTML comment content to post")
		fmt.Fprintln(os.Stderr, "    PLANE_SMOKE_STATE_ID    - State UUID to transition to")
		os.Exit(1)
	}

	safeURL := cfg.BaseURL
	safeToken := "<redacted>"

	logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
	logger.Info("starting plane smoke test",
		"base_url", safeURL,
		"token", safeToken,
		"workspace", cfg.WorkspaceSlug,
		"project_id", cfg.ProjectID,
		"work_item_id", cfg.WorkItemID,
		"apply", cfg.SmokeApply,
	)

	planeClient := plane.NewClient(plane.Config{
		BaseURL: cfg.BaseURL,
		Token:   cfg.Token,
	}, logger)

	if err := RunSmoke(context.Background(), cfg, planeClient, os.Stdout); err != nil {
		logger.Error("smoke test failed", "error", redactToken(err.Error(), cfg.Token))
		os.Exit(2)
	}

	logger.Info("plane smoke test finished successfully")
}

func redactToken(s, token string) string {
	if token == "" {
		return s
	}
	return strings.ReplaceAll(s, token, "<redacted>")
}
