package rest

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/features/analytics"
	"github.com/nexuscrm/backend/pkg/auth"
	"github.com/nexuscrm/shared/pkg/api"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type executionOnlyQueryService struct {
	reportRows []models.SObject
	rows       []models.SObject
}

func (s *executionOnlyQueryService) Query(_ context.Context, req api.QueryRequest, _ *models.UserSession) (*models.QueryResult, error) {
	if req.ObjectAPIName == constants.TableReport {
		return &models.QueryResult{Records: s.reportRows}, nil
	}
	return nil, nil
}

func (s *executionOnlyQueryService) QueryWithFilter(context.Context, string, string, *models.UserSession, string, string, int) (*models.QueryResult, error) {
	return nil, nil
}

func (s *executionOnlyQueryService) RunAnalytics(context.Context, models.AnalyticsQuery, *models.UserSession) (interface{}, error) {
	return nil, nil
}

func (s *executionOnlyQueryService) SearchSingleObject(context.Context, string, string, []string, []string, int, int, string, string, string, *models.UserSession) ([]models.SObject, error) {
	return nil, nil
}

func (s *executionOnlyQueryService) GlobalSearch(context.Context, string, *models.UserSession) ([]models.SearchResult, error) {
	return nil, nil
}

func (s *executionOnlyQueryService) Calculate(context.Context, string, models.SObject, *models.UserSession) (models.SObject, error) {
	return nil, nil
}

func (s *executionOnlyQueryService) ExecuteRawSQL(context.Context, string, []interface{}, *models.UserSession) ([]models.SObject, error) {
	return s.rows, nil
}

func TestAnalyticsHandler_RunReportExecution_AdHocContractOrder(t *testing.T) {
	gin.SetMode(gin.TestMode)

	reportSvc := analytics.NewReportService(&executionOnlyQueryService{
		rows: []models.SObject{
			{
				"AccountId_Name":         "Acme Corp",
				"OrderEffectiveDate":     "2026-04-01",
				"RecordTypeId_Name":      "Renewal",
				"AccountId_OwnerId_Name": "Alice Admin",
				"OrderStatus":            "Activated",
				"Product_Family__c":      "Database",
				"TCV_Amount__c":          1000.0,
				"ACV_Amount__c":          400.0,
				"Net_Fees__c":            125.0,
			},
			{
				"AccountId_Name":         "Acme Corp",
				"OrderEffectiveDate":     "2026-04-15",
				"RecordTypeId_Name":      "Expansion",
				"AccountId_OwnerId_Name": "Alice Admin",
				"OrderStatus":            "Pending",
				"Product_Family__c":      "AI",
				"TCV_Amount__c":          500.0,
				"ACV_Amount__c":          250.0,
				"Net_Fees__c":            75.0,
			},
		},
	}, nil, nil, nil)
	handler := NewAnalyticsHandler(reportSvc, nil, nil)

	body := map[string]interface{}{
		"reportMetadata": map[string]interface{}{
			"sourceObject": "ContractOrder",
			"reportType": map[string]interface{}{
				"type":  "ContractOrderList",
				"label": "ContractOrder",
			},
			"reportFormat": "SUMMARY",
			"detailColumns": []string{
				"RECORDTYPE",
				"ACCOUNT_OWNER",
				"ORDER_STATUS",
				"Order.Product_Family__c",
				"Order.TCV_Amount__c",
				"Order.ACV_Amount__c",
				"Order.Net_Fees__c",
			},
			"groupingsDown": []map[string]interface{}{
				{"name": "ACCOUNT_NAME", "sortOrder": "Asc", "dateGranularity": "NONE"},
				{"name": "ORDER_EFFECTIVE_DATE", "sortOrder": "Asc", "dateGranularity": "NONE"},
			},
		},
		"limit": 10,
	}
	bodyBytes, err := json.Marshal(body)
	require.NoError(t, err)

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("POST", "/analytics/report-execution", bytes.NewReader(bodyBytes))
	c.Request.Header.Set("Content-Type", "application/json")
	c.Set(constants.ContextKeyUser, auth.UserSession{
		ID:        "user-1",
		ProfileId: constants.ProfileSystemAdmin,
	})

	handler.RunReportExecution(c)

	require.Equal(t, http.StatusOK, w.Code)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))

	reportMetadata := resp["reportMetadata"].(map[string]interface{})
	assert.Equal(t, "SUMMARY", reportMetadata["reportFormat"])
	assert.Equal(t, "ContractOrder", reportMetadata["sourceObject"])
	assert.Equal(t, []interface{}{
		"RECORDTYPE",
		"ACCOUNT_OWNER",
		"ORDER_STATUS",
		"Order.Product_Family__c",
		"Order.TCV_Amount__c",
		"Order.ACV_Amount__c",
		"Order.Net_Fees__c",
	}, reportMetadata["detailColumns"])

	groupingsDown := resp["groupingsDown"].(map[string]interface{})
	topGroups := groupingsDown["groupings"].([]interface{})
	assert.Len(t, topGroups, 1)

	firstGroup := topGroups[0].(map[string]interface{})
	assert.Equal(t, "Acme Corp", firstGroup["value"])
	childGroups := firstGroup["groupings"].([]interface{})
	assert.Len(t, childGroups, 2)
	assert.Equal(t, "0_0", childGroups[0].(map[string]interface{})["key"])
	assert.Equal(t, "0_1", childGroups[1].(map[string]interface{})["key"])

	factMap := resp["factMap"].(map[string]interface{})
	accountSubtotal := factMap["0!T"].(map[string]interface{})
	accountAggs := accountSubtotal["aggregates"].([]interface{})
	assert.Equal(t, float64(2), accountAggs[0].(map[string]interface{})["value"])

	leaf := factMap["0_0!T"].(map[string]interface{})
	rows := leaf["rows"].([]interface{})
	cells := rows[0].(map[string]interface{})["dataCells"].([]interface{})
	assert.Equal(t, "Renewal", cells[0].(map[string]interface{})["label"])
	assert.Equal(t, "Alice Admin", cells[1].(map[string]interface{})["label"])
	assert.Equal(t, "Activated", cells[2].(map[string]interface{})["label"])
	assert.Equal(t, "Database", cells[3].(map[string]interface{})["label"])

	secondLeafAggs := factMap["0_1!T"].(map[string]interface{})["aggregates"].([]interface{})
	assert.Equal(t, float64(1), secondLeafAggs[0].(map[string]interface{})["value"])
}

func TestAnalyticsHandler_RunReport_SavedContractOrderPreservesStoredColumns(t *testing.T) {
	gin.SetMode(gin.TestMode)

	reportSvc := analytics.NewReportService(&executionOnlyQueryService{
		reportRows: []models.SObject{
			{
				"Id":           "00O5g00000AL27REAT",
				"Name":         "Contracts & Orders",
				"SourceObject": "ContractOrder",
				"ReportType":   "Summary",
				"Format":       "Summary",
				"Columns":      []interface{}{"RECORDTYPE", "ACCOUNT_OWNER", "ORDER_STATUS", "Order.Product_Family__c", "Order.TCV_Amount__c", "Order.ACV_Amount__c", "Order.Net_Fees__c"},
				"Grouping": []interface{}{
					map[string]interface{}{"name": "ACCOUNT_NAME", "dateGranularity": "None", "sortOrder": "Asc"},
					map[string]interface{}{"name": "ORDER_EFFECTIVE_DATE", "dateGranularity": "FiscalQuarter", "sortOrder": "Asc"},
				},
				"Aggregates": []interface{}{"s!Order.TCV_Amount__c", "s!Order.ACV_Amount__c", "s!Order.Net_Fees__c", "RowCount"},
			},
		},
		rows: []models.SObject{
			{
				"AccountId_Name":         "Acme Corp",
				"OrderEffectiveDate":     "2026-04-01",
				"RecordTypeId_Name":      "Renewal",
				"AccountId_OwnerId_Name": "Alice Admin",
				"OrderStatus":            "Activated",
				"Product_Family__c":      "Database",
				"TCV_Amount__c":          1000.0,
				"ACV_Amount__c":          400.0,
				"Net_Fees__c":            125.0,
			},
		},
	}, nil, nil, nil)
	handler := NewAnalyticsHandler(reportSvc, nil, nil)

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/analytics/reports/00O5g00000AL27REAT", nil)
	c.Params = gin.Params{{Key: "reportId", Value: "00O5g00000AL27REAT"}}
	c.Set(constants.ContextKeyUser, auth.UserSession{
		ID:        "user-1",
		ProfileId: constants.ProfileSystemAdmin,
	})

	handler.RunReport(c)

	require.Equal(t, http.StatusOK, w.Code)

	var resp map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))

	reportMetadata := resp["reportMetadata"].(map[string]interface{})
	assert.Equal(t, "SUMMARY", reportMetadata["reportFormat"])
	assert.Equal(t, "ContractOrder", reportMetadata["sourceObject"])
	assert.Equal(t, []interface{}{
		"RECORDTYPE",
		"ACCOUNT_OWNER",
		"ORDER_STATUS",
		"Order.Product_Family__c",
		"Order.TCV_Amount__c",
		"Order.ACV_Amount__c",
		"Order.Net_Fees__c",
	}, reportMetadata["detailColumns"])

	groupingsDown := resp["groupingsDown"].(map[string]interface{})
	topGroups := groupingsDown["groupings"].([]interface{})
	assert.Len(t, topGroups, 1)

	firstGroup := topGroups[0].(map[string]interface{})
	assert.Equal(t, "Acme Corp", firstGroup["value"])
	childGroups := firstGroup["groupings"].([]interface{})
	assert.Len(t, childGroups, 1)
	assert.Equal(t, "0_0", childGroups[0].(map[string]interface{})["key"])

	factMap := resp["factMap"].(map[string]interface{})
	group := factMap["0!T"].(map[string]interface{})
	assert.Empty(t, group["rows"].([]interface{}))

	leaf := factMap["0_0!T"].(map[string]interface{})
	rows := leaf["rows"].([]interface{})
	cells := rows[0].(map[string]interface{})["dataCells"].([]interface{})
	assert.Equal(t, "Renewal", cells[0].(map[string]interface{})["label"])
	assert.Equal(t, "Alice Admin", cells[1].(map[string]interface{})["label"])
	assert.Equal(t, "Activated", cells[2].(map[string]interface{})["label"])
}
