//go:build integration

package persistence

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestDashboardPersistence_Integration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// Setup
	schemaRepo := setupTestDB(t)
	db := schemaRepo.db
	metadataRepo := NewMetadataRepository(db)
	ctx := context.Background()

	// 0. Ensure Tables Exist (Mock Schema for Integration Test context)
	// We use 'id' for Dashboard because constants.FieldID ('id') is used in code without mapping layer in MetadataRepo
	_, _ = db.Exec("DROP TABLE IF EXISTS Dashboard")
	_, err := db.Exec(`CREATE TABLE Dashboard (
		Id VARCHAR(255) PRIMARY KEY,
		Name VARCHAR(255),
		DeveloperName VARCHAR(255),
		Description TEXT,
		FolderId VARCHAR(255),
		FilterMode VARCHAR(50),
		LayoutMode VARCHAR(50),
		StartDate DATE,
		EndDate DATE,
		DateRangeType VARCHAR(50),
		LayoutType VARCHAR(50) DEFAULT 'Grid',
		DashboardType VARCHAR(50),
		RunningUserId VARCHAR(255),
		IsDeleted TINYINT DEFAULT 0,
		CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
		LastModifiedDate DATETIME DEFAULT CURRENT_TIMESTAMP
	)`)
	require.NoError(t, err)

	_, _ = db.Exec("DROP TABLE IF EXISTS DashboardComponent")
	_, err = db.Exec(`CREATE TABLE DashboardComponent (
		Id VARCHAR(255) PRIMARY KEY,
		DashboardId VARCHAR(255),
		ReportId VARCHAR(255),
		Type VARCHAR(50),
		Title VARCHAR(255),
		Config TEXT,
		IsDeleted TINYINT DEFAULT 0
	)`)
	require.NoError(t, err)

	// Clean up logic is redundant if we drop/create, but good for post-test consistency.
	// But we need to clean up AFTER the test too? The defer block handles post-test cleanup.
	// We just removed the explicit 'DELETE FROM' block here as it's replaced by DROP/CREATE.

	// 1. Create Reports (Prerequisites for Components)
	reportID1 := fmt.Sprintf("rep_%d_1", time.Now().UnixNano())
	reportID2 := fmt.Sprintf("rep_%d_2", time.Now().UnixNano())

	// Insert Reports into Report manually for speed
	// Note: Report should technically be created via standard schema, but we assume it exists or we mock it?
	// The repo likely relies on it. For this test self-containment, we should probably mock it too or assume existing.
	// If we assume existing, we must use whatever schema it has.
	// Ideally we use standard column names.
	_, err = db.Exec("INSERT INTO Report (Id, Name, DeveloperName, SourceObject, ReportType) VALUES (?, ?, ?, ?, ?)",
		reportID1, "Test Report 1", "Test_Report_1", "Account", "Tabular")
	require.NoError(t, err)

	_, err = db.Exec("INSERT INTO Report (Id, Name, DeveloperName, SourceObject, ReportType) VALUES (?, ?, ?, ?, ?)",
		reportID2, "Test Report 2", "Test_Report_2", "Contact", "Summary")
	require.NoError(t, err)

	defer func() {
		_, _ = db.Exec("DELETE FROM Report WHERE Id IN (?, ?)", reportID1, reportID2)
	}()

	// 2. Define Dashboard with Widgets
	dashID := fmt.Sprintf("dash_%d", time.Now().UnixNano())
	t.Logf("Generated Dashboard ID: %s", dashID)
	dashLabel := "Integration Test Dashboard"

	widget1 := models.WidgetConfig{
		Title:    "Widget 1",
		Type:     "Chart",
		ReportID: reportID1,
	}
	widget2 := models.WidgetConfig{
		Title:    "Widget 2",
		Type:     "Metric",
		ReportID: reportID2,
	}

	dashboard := &models.DashboardConfig{
		ID:    dashID,
		Label: dashLabel,
		Widgets: []models.WidgetConfig{
			widget1,
			widget2,
		},
	}

	// Cleanup Dashboard
	defer func() {
		_, _ = db.Exec("DELETE FROM Dashboard WHERE Id = ?", dashID)
		_, _ = db.Exec("DELETE FROM DashboardComponent WHERE dashboard_id = ?", dashID)
	}()

	// 3. Create Dashboard
	t.Log("Creating Dashboard with 2 Widgets...")
	err = metadataRepo.CreateDashboard(ctx, dashboard)
	require.NoError(t, err, "CreateDashboard failed")

	// 4. Verify Creation (Fetch)
	t.Log("Fetching Dashboard...")
	fetchedDash, err := metadataRepo.GetDashboard(ctx, dashID)
	require.NoError(t, err, "GetDashboard failed")
	require.NotNil(t, fetchedDash, "GetDashboard returned nil result (ErrNoRows?)")
	assert.Equal(t, dashLabel, fetchedDash.Label)

	// CRITICAL: This is expected to FAIL until fix is implemented
	assert.Len(t, fetchedDash.Widgets, 2, "Expected 2 widgets to be persisted")

	// Helper to find widget by title
	findWidget := func(widgets []models.WidgetConfig, title string) *models.WidgetConfig {
		for _, w := range widgets {
			if w.Title == title {
				return &w
			}
		}
		return nil
	}

	w1 := findWidget(fetchedDash.Widgets, "Widget 1")
	require.NotNil(t, w1, "Widget 1 not found")
	assert.Equal(t, reportID1, w1.ReportID)

	w2 := findWidget(fetchedDash.Widgets, "Widget 2")
	require.NotNil(t, w2, "Widget 2 not found")
	assert.Equal(t, reportID2, w2.ReportID)

	// 5. Update Dashboard (Replace Widgets)
	t.Log("Updating Dashboard (Replacing Widgets)...")
	// Remove Widget 1, Keep Widget 2, Add Widget 3
	widget3 := models.WidgetConfig{
		Title:    "Widget 3",
		Type:     "Table",
		ReportID: reportID1, // Reuse Test Report 1
	}
	updatedDashboard := &models.DashboardConfig{
		Label: dashLabel + " Updated",
		Widgets: []models.WidgetConfig{
			widget2, // Keep
			widget3, // New
		},
	}

	err = metadataRepo.UpdateDashboard(ctx, dashID, updatedDashboard)
	assert.NoError(t, err)

	// 6. Verify Update
	t.Log("Fetching Updated Dashboard...")
	fetchedUpdatedDash, err := metadataRepo.GetDashboard(ctx, dashID)
	assert.NoError(t, err)
	assert.Equal(t, dashLabel+" Updated", fetchedUpdatedDash.Label)

	// CRITICAL: This is expected to FAIL until fix is implemented
	assert.Len(t, fetchedUpdatedDash.Widgets, 2, "Expected 2 widgets after update")
	if len(fetchedUpdatedDash.Widgets) == 2 {
		titles := []string{fetchedUpdatedDash.Widgets[0].Title, fetchedUpdatedDash.Widgets[1].Title}
		assert.Contains(t, titles, "Widget 2")
		assert.Contains(t, titles, "Widget 3")
		assert.NotContains(t, titles, "Widget 1")
	}
}
