package devops

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/require"
)

// operationsYAML mirrors the shape of the operations.yaml the DevOps server is
// deployed with, including the fields whose YAML and JSON names differ.
const operationsYAML = `
- name: RestartChangefeed
  alias: Restart Changefeed
  target: undefined
  description: restart a changefeed
  categories: ["cdc-meta", "changefeed-restart"]
  actions:
  - name: RestartChangefeed
    preferStartPolicy: manual
  params:
    changefeed_id:
      type: string
    cluster_type:
      type: string
      enum: ["CHANGEFEED", "CHANGEFEED_NG"]
    oncall_category:
      type: string
      const: cdc
    components:
      type: string
      optional: true
      multiEnum: ["tidb", "tikv"]
  approval:
    nodes:
    - userGroup: cloud_ticdc_operation
      method: ANY

- name: ShutdownOperationForK8sUpgrade
  target: cluster
  internal: true
  categories: ["dedicated"]
  actions:
  - name: Shutdown
  params:
    cluster_id:
      type: string
  approval:
    nodes:
    - fromParam:
        paramName: tenant_region
        paramValueMapping:
          NA:
            userGroup: cloud-operation-region-admin-na
        defaultApprover:
          userGroup: ops-portal-admin
      method: ANY
      condition: '!(tenant_region in ["TEST_INFRA"])'
`

func writeCatalogFile(t *testing.T) string {
	t.Helper()

	path := filepath.Join(t.TempDir(), "operations.yaml")
	require.NoError(t, os.WriteFile(path, []byte(operationsYAML), 0o600))
	return path
}

func TestLoadCatalogFromFile(t *testing.T) {
	catalog, err := LoadCatalogFromFile(writeCatalogFile(t))
	require.NoError(t, err)
	require.Equal(t, []string{"RestartChangefeed", "ShutdownOperationForK8sUpgrade"}, catalog.Names())

	op, ok := catalog.Get("RestartChangefeed")
	require.True(t, ok)
	require.Equal(t, TargetUndefined, op.Target)
	require.Equal(t, "Restart Changefeed", op.Alias)
	require.False(t, op.Internal)

	require.Len(t, op.Actions, 1)
	require.NotNil(t, op.Actions[0].PreferStartPolicy)
	require.Equal(t, StartPolicyManual, *op.Actions[0].PreferStartPolicy)

	require.Equal(t, []string{"CHANGEFEED", "CHANGEFEED_NG"}, op.Params["cluster_type"].Enum)
	require.Equal(t, []string{"tidb", "tikv"}, op.Params["components"].MultiEnum)
	require.NotNil(t, op.Params["oncall_category"].Const)
	require.Equal(t, "cdc", *op.Params["oncall_category"].Const)
}

func TestCatalogListFiltersInternalByDefault(t *testing.T) {
	catalog, err := LoadCatalogFromFile(writeCatalogFile(t))
	require.NoError(t, err)

	visible := catalog.List(CatalogFilter{})
	require.Len(t, visible, 1)
	require.Equal(t, "RestartChangefeed", visible[0].Name)

	all := catalog.List(CatalogFilter{IncludeInternal: true})
	require.Len(t, all, 2)

	byCategory := catalog.List(CatalogFilter{Category: "cdc-meta"})
	require.Len(t, byCategory, 1)

	byTarget := catalog.List(CatalogFilter{Target: "cluster", IncludeInternal: true})
	require.Len(t, byTarget, 1)
	require.Equal(t, "ShutdownOperationForK8sUpgrade", byTarget[0].Name)
}

func TestApprovalSummarize(t *testing.T) {
	catalog, err := LoadCatalogFromFile(writeCatalogFile(t))
	require.NoError(t, err)

	op, _ := catalog.Get("RestartChangefeed")
	chain := op.Approval.Summarize()
	require.Len(t, chain, 1)
	require.Equal(t, "ANY", chain[0].Method)
	require.Equal(t, []string{"group:cloud_ticdc_operation"}, chain[0].Approvers)

	internal, _ := catalog.Get("ShutdownOperationForK8sUpgrade")
	fromParam := internal.Approval.Summarize()
	require.Len(t, fromParam, 1)
	require.Equal(t, []string{
		"tenant_region=NA -> group:cloud-operation-region-admin-na",
		"default -> group:ops-portal-admin",
	}, fromParam[0].Approvers)
	require.Contains(t, fromParam[0].Condition, "TEST_INFRA")
}

func TestLoadCatalogFromFileRejectsEmpty(t *testing.T) {
	path := filepath.Join(t.TempDir(), "empty.yaml")
	require.NoError(t, os.WriteFile(path, []byte("[]\n"), 0o600))

	_, err := LoadCatalogFromFile(path)
	require.ErrorContains(t, err, "no operation definitions")
}
