package serverruntime

import (
	"go/parser"
	"go/token"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"testing"
)

const crmDistributionImport = "github.com/nexuscrm/backend/distributions/crm"

func TestProductDistributionCommandMatrixIsExplicit(t *testing.T) {
	cases := []struct {
		name                    string
		path                    string
		wantBuildConstraint     string
		wantDistributionImports []string
		wantSnippets            []string
	}{
		{
			name:                    "distribution setup default is app blind",
			path:                    repoPath("backend", "distributions", "cmd", "setup", "distribution_default.go"),
			wantBuildConstraint:     "!crm",
			wantDistributionImports: nil,
			wantSnippets:            []string{"serverruntime.NoopProductDistribution()"},
		},
		{
			name:                    "setup CRM owns only CRM product",
			path:                    repoPath("backend", "distributions", "cmd", "setup", "distribution_crm.go"),
			wantBuildConstraint:     "crm",
			wantDistributionImports: []string{crmDistributionImport},
			wantSnippets:            []string{"serverruntime.NewProductDistribution(crmdistribution.ProductHooks())"},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			data, err := os.ReadFile(tc.path)
			if err != nil {
				t.Fatalf("read %s: %v", tc.path, err)
			}
			if got := goBuildConstraintForDistributionMatrix(string(data)); got != tc.wantBuildConstraint {
				t.Fatalf("%s go:build constraint = %q, want %q", filepath.ToSlash(tc.path), got, tc.wantBuildConstraint)
			}
			if got := distributionImportsForFile(t, tc.path); !reflect.DeepEqual(got, tc.wantDistributionImports) {
				t.Fatalf("%s distribution imports = %#v, want %#v", filepath.ToSlash(tc.path), got, tc.wantDistributionImports)
			}
			compact := compactSourceForDistributionMatrix(string(data))
			for _, want := range tc.wantSnippets {
				if !strings.Contains(compact, compactSourceForDistributionMatrix(want)) {
					t.Fatalf("%s missing expected product wiring snippet %q", filepath.ToSlash(tc.path), want)
				}
			}
		})
	}
}

func TestProductDistributionCommandMatrixBuilds(t *testing.T) {
	cases := []struct {
		name            string
		tags            string
		pkg             string
		requiresAppPack bool
	}{
		{name: "default setup", pkg: "./cmd/setup"},
		{name: "crm setup", tags: "crm", pkg: "./cmd/setup", requiresAppPack: true},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.requiresAppPack && !appPacksDialectSourceAvailable(t) {
				t.Skip("app-packs dialect source checkout is unavailable; CI enforces this distribution matrix with app-packs checked out")
			}
			args := []string{"list"}
			if strings.TrimSpace(tc.tags) != "" {
				args = append(args, "-tags", tc.tags)
			}
			args = append(args, tc.pkg)
			cmd := exec.Command("go", args...)
			if tc.requiresAppPack {
				cmd.Dir = repoPath("backend", "distributions")
				cmd.Env = append(os.Environ(), "GOWORK=off")
			} else {
				cmd.Dir = repoPath("backend")
			}
			out, err := cmd.CombinedOutput()
			if err != nil {
				t.Fatalf("go %s: %v\n%s", strings.Join(args, " "), err, out)
			}
		})
	}
}

func distributionImportsForFile(t *testing.T, path string) []string {
	t.Helper()
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
	if err != nil {
		t.Fatalf("parse %s: %v", path, err)
	}
	var out []string
	for _, spec := range file.Imports {
		imp, err := strconv.Unquote(spec.Path.Value)
		if err != nil {
			t.Fatalf("unquote import %s in %s: %v", spec.Path.Value, path, err)
		}
		if strings.HasPrefix(imp, "github.com/nexuscrm/backend/distributions/") {
			out = append(out, imp)
		}
	}
	sort.Strings(out)
	return out
}

func goBuildConstraintForDistributionMatrix(src string) string {
	for _, line := range strings.Split(src, "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "//go:build ") {
			return strings.TrimSpace(strings.TrimPrefix(line, "//go:build "))
		}
		if strings.HasPrefix(line, "package ") {
			return ""
		}
	}
	return ""
}

func compactSourceForDistributionMatrix(src string) string {
	return strings.Join(strings.Fields(src), "")
}
