package dialectsfcrm

import (
	"bytes"
	"context"
	"errors"
	"log"
	"net/http"
	"strings"
	"testing"

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
)

func TestFormulaViewStatusNeutralHandlerSuccess(t *testing.T) {
	maintenance := &fakeFormulaViewMaintenance{
		status: &contract.FormulaViewStatusResult{
			Object:             "WorkItem",
			ViewExists:         true,
			ViewName:           "v_WorkItem",
			ActiveFormulaCount: 2,
			FrozenFormulaCount: 1,
			ResolvedTableName:  "v_workitem",
		},
	}
	resp, err := FormulaViewStatusNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewStatusNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200", resp.Status)
	}
	assertBody(t, "formula-view-status", resp.Body, "",
		`{"activeFormulaCount":2,"frozenFormulaCount":1,"object":"WorkItem","resolvedTableName":"v_workitem","viewExists":true,"viewName":"v_WorkItem"}`)
}

func TestFormulaViewValidateNeutralHandlerDiagnosticResult(t *testing.T) {
	maintenance := &fakeFormulaViewMaintenance{
		validate: &contract.FormulaViewValidationResult{Valid: false, Error: "unresolved cross-reference Parent.Name: missing metadata join"},
	}
	resp, err := FormulaViewValidateNeutralHandler(context.Background(), formulaViewRequest("ObjA", []byte(`{"formula":"Parent.Name","returnType":"Text"}`)), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewValidateNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200", resp.Status)
	}
	assertBody(t, "formula-view-validate-diagnostic", resp.Body, "",
		`{"error":"unresolved cross-reference Parent.Name: missing metadata join","valid":false}`)
}

func TestFormulaViewDDLNotFoundProjectsV63NotFound(t *testing.T) {
	maintenance := &fakeFormulaViewMaintenance{
		ddlErr: &contract.MetadataMaintenanceError{
			Kind:    contract.MetadataMaintenanceErrorNotFound,
			Message: "formula view for object \"WorkItem\" not found",
		},
	}
	resp, err := FormulaViewDDLNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewDDLNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusNotFound {
		t.Fatalf("status = %d, want 404", resp.Status)
	}
	want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeNotFound, "formula view for object \"WorkItem\" not found"))
	assertBody(t, "formula-view-ddl-not-found", resp.Body, want, want)
}

func TestFormulaViewRebuildInternalErrorIsSanitized(t *testing.T) {
	var logs bytes.Buffer
	restore := captureFormulaViewLogs(t, &logs)
	defer restore()

	maintenance := &fakeFormulaViewMaintenance{rebuildErr: errors.New("ddl contains private table name")}
	resp, err := FormulaViewRebuildNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewRebuildNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500", resp.Status)
	}
	want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInternalError, formulaViewGenericInternalErrorMessage))
	assertBody(t, "formula-view-rebuild-internal", resp.Body, want, want)
	if strings.Contains(mustJSON(t, resp.Body), "private table") {
		t.Fatalf("internal formula-view response leaked cause: %s", mustJSON(t, resp.Body))
	}
	if !strings.Contains(logs.String(), "ddl contains private table name") {
		t.Fatalf("logs = %q, want unknown internal cause", logs.String())
	}
}

func TestFormulaViewMaintenanceInternalKindIsLoggedAndSanitized(t *testing.T) {
	var logs bytes.Buffer
	restore := captureFormulaViewLogs(t, &logs)
	defer restore()

	maintenance := &fakeFormulaViewMaintenance{rebuildErr: &contract.MetadataMaintenanceError{
		Kind:    contract.MetadataMaintenanceErrorInternal,
		Message: "formula view rebuild failed",
		Cause:   errors.New("secret ddl failure against table _secret_formula_store password=secret"),
	}}
	resp, err := FormulaViewRebuildNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewRebuildNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500", resp.Status)
	}
	raw := mustJSON(t, resp.Body)
	for _, leaked := range []string{"_secret_formula_store", "password=secret", "secret ddl failure", "formula view rebuild failed"} {
		if strings.Contains(raw, leaked) {
			t.Fatalf("internal-kind response leaked %q: %s", leaked, raw)
		}
	}
	want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInternalError, formulaViewGenericInternalErrorMessage))
	assertBody(t, "formula-view-internal-kind", resp.Body, want, want)
	if !strings.Contains(logs.String(), "_secret_formula_store") || !strings.Contains(logs.String(), "password=secret") || !strings.Contains(logs.String(), "secret ddl failure") {
		t.Fatalf("logs = %q, want internal maintenance cause", logs.String())
	}
}

func TestFormulaViewRebuildTypedClientErrorsArePreserved(t *testing.T) {
	maintenance := &fakeFormulaViewMaintenance{rebuildErr: &contract.MetadataMaintenanceError{
		Kind:    contract.MetadataMaintenanceErrorInvalid,
		Message: "formula view rebuild request is invalid",
	}}
	resp, err := FormulaViewRebuildNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewRebuildNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusBadRequest {
		t.Fatalf("status = %d, want 400", resp.Status)
	}
	want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInvalidRequest, "formula view rebuild request is invalid"))
	assertBody(t, "formula-view-rebuild-invalid", resp.Body, want, want)
}

func TestFormulaViewRebuildAllInternalErrorIsSanitized(t *testing.T) {
	var logs bytes.Buffer
	restore := captureFormulaViewLogs(t, &logs)
	defer restore()

	maintenance := &fakeFormulaViewMaintenance{rebuildAllErr: errors.New("CREATE VIEW v_WorkItem AS SELECT secret_token FROM private_table")}
	resp, err := FormulaViewRebuildAllNeutralHandler(context.Background(), &dialect.Request{}, formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewRebuildAllNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500", resp.Status)
	}
	raw := mustJSON(t, resp.Body)
	for _, leaked := range []string{"CREATE VIEW", "secret_token", "private_table"} {
		if strings.Contains(raw, leaked) {
			t.Fatalf("rebuild-all response leaked %q: %s", leaked, raw)
		}
	}
	want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInternalError, formulaViewGenericInternalErrorMessage))
	assertBody(t, "formula-view-rebuild-all-internal", resp.Body, want, want)
	if !strings.Contains(logs.String(), "CREATE VIEW") || !strings.Contains(logs.String(), "private_table") {
		t.Fatalf("logs = %q, want rebuild-all diagnostics", logs.String())
	}
}

func TestFormulaViewServiceUnavailableErrorsAreSanitized(t *testing.T) {
	t.Run("missing contract", func(t *testing.T) {
		resp, err := FormulaViewStatusNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), dialect.Deps{})
		if err != nil {
			t.Fatalf("FormulaViewStatusNeutralHandler: %v", err)
		}
		if resp.Status != http.StatusServiceUnavailable {
			t.Fatalf("status = %d, want 503", resp.Status)
		}
		want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInternalError, formulaViewGenericInternalErrorMessage))
		assertBody(t, "formula-view-missing-contract", resp.Body, want, want)
	})

	t.Run("unavailable maintenance", func(t *testing.T) {
		maintenance := &fakeFormulaViewMaintenance{statusErr: &contract.MetadataMaintenanceError{
			Kind:    contract.MetadataMaintenanceErrorUnavailable,
			Message: "formula DDL service unavailable: tcp 10.0.0.12 password=secret",
		}}
		resp, err := FormulaViewStatusNeutralHandler(context.Background(), formulaViewRequest("WorkItem", nil), formulaViewDeps(maintenance))
		if err != nil {
			t.Fatalf("FormulaViewStatusNeutralHandler: %v", err)
		}
		if resp.Status != http.StatusServiceUnavailable {
			t.Fatalf("status = %d, want 503", resp.Status)
		}
		raw := mustJSON(t, resp.Body)
		for _, leaked := range []string{"10.0.0.12", "password=secret", "formula DDL service unavailable"} {
			if strings.Contains(raw, leaked) {
				t.Fatalf("unavailable response leaked %q: %s", leaked, raw)
			}
		}
		want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInternalError, formulaViewGenericInternalErrorMessage))
		assertBody(t, "formula-view-unavailable", resp.Body, want, want)
	})
}

func TestFormulaViewValidateInvalidRequest(t *testing.T) {
	maintenance := &fakeFormulaViewMaintenance{}
	resp, err := FormulaViewValidateNeutralHandler(context.Background(), formulaViewRequest("Work Item", []byte(`{"formula":"Name"}`)), formulaViewDeps(maintenance))
	if err != nil {
		t.Fatalf("FormulaViewValidateNeutralHandler: %v", err)
	}
	if resp.Status != http.StatusBadRequest {
		t.Fatalf("status = %d, want 400", resp.Status)
	}
	want := mustJSON(t, wire.SFErrorResponse(wire.SFCodeInvalidRequest, "objectName contains invalid characters"))
	assertBody(t, "formula-view-invalid-object", resp.Body, want, want)
}

func formulaViewRequest(objectName string, body []byte) *dialect.Request {
	return &dialect.Request{
		Params: map[string]string{"objectName": objectName},
		Body:   body,
	}
}

func formulaViewDeps(maintenance contract.MetadataMaintenance) dialect.Deps {
	return dialect.Deps{Contracts: contract.Deps{MetadataMaintenance: maintenance}}
}

func captureFormulaViewLogs(t *testing.T, logs *bytes.Buffer) func() {
	t.Helper()
	oldWriter := log.Writer()
	oldFlags := log.Flags()
	log.SetOutput(logs)
	log.SetFlags(0)
	return func() {
		log.SetOutput(oldWriter)
		log.SetFlags(oldFlags)
	}
}

type fakeFormulaViewMaintenance struct {
	status        *contract.FormulaViewStatusResult
	statusErr     error
	ddl           *contract.FormulaViewDDLResult
	ddlErr        error
	rebuild       *contract.FormulaViewRebuildResult
	rebuildErr    error
	rebuildAllErr error
	validate      *contract.FormulaViewValidationResult
	validateErr   error
}

func (*fakeFormulaViewMaintenance) RefreshAutoNumberMetadata(context.Context) (*contract.AutoNumberMetadataRefreshResult, error) {
	return nil, nil
}

func (f *fakeFormulaViewMaintenance) FormulaViewStatus(context.Context, string) (*contract.FormulaViewStatusResult, error) {
	return f.status, f.statusErr
}

func (f *fakeFormulaViewMaintenance) PreviewFormulaViewDDL(context.Context, string) (*contract.FormulaViewDDLResult, error) {
	return f.ddl, f.ddlErr
}

func (f *fakeFormulaViewMaintenance) RebuildFormulaView(context.Context, string) (*contract.FormulaViewRebuildResult, error) {
	return f.rebuild, f.rebuildErr
}

func (f *fakeFormulaViewMaintenance) RebuildAllFormulaViews(context.Context) (*contract.FormulaViewRebuildAllResult, error) {
	if f.rebuildAllErr != nil {
		return nil, f.rebuildAllErr
	}
	return &contract.FormulaViewRebuildAllResult{}, nil
}

func (f *fakeFormulaViewMaintenance) ValidateFormulaView(context.Context, contract.FormulaViewValidationRequest) (*contract.FormulaViewValidationResult, error) {
	return f.validate, f.validateErr
}
