package tests

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	tests "github.com/jfraeys/fetch_ml/tests/fixtures"
)

// TestPodmanIntegration tests podman workflow with examples
func TestPodmanIntegration(t *testing.T) {
	if os.Getenv("FETCH_ML_E2E_PODMAN") != "1" {
		t.Skip("Skipping PodmanIntegration (set FETCH_ML_E2E_PODMAN=1 to enable)")
	}

	if testing.Short() {
		t.Skip("Skipping podman integration test in short mode")
	}

	// Check if podman is available
	if _, err := exec.LookPath("podman"); err != nil {
		t.Skip("Podman not available, skipping integration test")
	}

	// Check if podman daemon is running
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	podmanCheck := exec.CommandContext(ctx, "podman", "info")
	if err := podmanCheck.Run(); err != nil {
		t.Skip("Podman daemon not running, skipping integration test")
	}

	// Determine project root (two levels up from tests/e2e)
	projectRoot, err := filepath.Abs(filepath.Join("..", ".."))
	if err != nil {
		t.Fatalf("Failed to resolve project root: %v", err)
	}

	// Test build
	t.Run("BuildContainer", func(t *testing.T) {
		if os.Getenv("FETCH_ML_E2E_PODMAN_REBUILD") != "1" {
			// Fast path: reuse existing image.
			checkCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
			defer cancel()
			// `podman image exists <name>` exits 0 if present, 1 if missing.
			check := exec.CommandContext(checkCtx, "podman", "image", "exists", "secure-ml-runner:test")
			if err := check.Run(); err == nil {
				t.Log("Podman image secure-ml-runner:test already exists; skipping rebuild")
				return
			}
		}

		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
		defer cancel()

		//nolint:gosec // G204: Subprocess launched with potential tainted input - this is a test
		cmd := exec.CommandContext(ctx, "podman", "build",
			"-f", filepath.Join("podman", "secure-ml-runner.podfile"),
			"-t", "secure-ml-runner:test",
			"podman")

		cmd.Dir = projectRoot
		t.Logf("Building container with command: %v", cmd)
		t.Logf("Current directory: %s", cmd.Dir)
		output, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("Failed to build container: %v\nOutput: %s", err, string(output))
		}

		t.Logf("Container build successful")
	})

	// Test execution with examples
	t.Run("ExecuteExample", func(t *testing.T) {
		examplesDir := tests.NewExamplesDir(filepath.Join("..", "fixtures", "examples"))

		type tc struct {
			name         string
			project      string
			depsFile     string
			preparePodIn func(ctx context.Context, workspaceDir, project string) error
		}

		cases := []tc{
			{
				name:     "RequirementsTxt",
				project:  "standard_ml_project",
				depsFile: "requirements.txt",
			},
			{
				name:     "PyprojectToml",
				project:  "pyproject_project",
				depsFile: "pyproject.toml",
			},
			{
				name:     "PoetryLock",
				project:  "poetry_project",
				depsFile: "poetry.lock",
				preparePodIn: func(ctx context.Context, workspaceDir, project string) error {
					// Generate lock inside container so it matches the container's Poetry/version.
					//nolint:gosec // G204: Subprocess launched with potential tainted input - this is a test
					cmd := exec.CommandContext(ctx, "podman", "run", "--rm",
						"--security-opt", "no-new-privileges",
						"--cap-drop", "ALL",
						"--memory", "2g",
						"--cpus", "1",
						"--userns", "keep-id",
						"-v", workspaceDir+":/workspace:rw",
						"-w", "/workspace/"+project,
						"--entrypoint", "conda",
						"secure-ml-runner:test",
						"run", "-n", "ml_env", "poetry", "lock",
					)
					cmd.Dir = ".."
					out, err := cmd.CombinedOutput()
					if err != nil {
						return fmt.Errorf("failed to generate poetry.lock: %v\nOutput: %s", err, string(out))
					}
					return nil
				},
			},
		}

		for _, c := range cases {
			c := c
			t.Run(c.name, func(t *testing.T) {
				tempDir := t.TempDir()
				workspaceDir := filepath.Join(tempDir, "workspace")
				resultsDir := filepath.Join(tempDir, "results")

				if err := os.MkdirAll(workspaceDir, 0750); err != nil {
					t.Fatalf("Failed to create workspace directory: %v", err)
				}
				if err := os.MkdirAll(resultsDir, 0750); err != nil {
					t.Fatalf("Failed to create results directory: %v", err)
				}

				dstDir := filepath.Join(workspaceDir, c.project)
				if err := examplesDir.CopyProject(c.project, dstDir); err != nil {
					t.Fatalf("Failed to copy example project: %v (dst: %s)", err, dstDir)
				}

				ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
				defer cancel()

				if c.preparePodIn != nil {
					if err := c.preparePodIn(ctx, workspaceDir, c.project); err != nil {
						t.Fatal(err)
					}
				}

				//nolint:gosec // G204: Subprocess launched with potential tainted input - this is a test
				cmd := exec.CommandContext(ctx, "podman", "run", "--rm",
					"--security-opt", "no-new-privileges",
					"--cap-drop", "ALL",
					"--memory", "2g",
					"--cpus", "1",
					"--userns", "keep-id",
					"-v", workspaceDir+":/workspace:rw",
					"-v", resultsDir+":/workspace/results:rw",
					"secure-ml-runner:test",
					"--workspace", "/workspace/"+c.project,
					"--deps", "/workspace/"+c.project+"/"+c.depsFile,
					"--script", "/workspace/"+c.project+"/train.py",
					"--args", "--output_dir", "/workspace/results",
				)

				cmd.Dir = ".."
				output, err := cmd.CombinedOutput()
				if err != nil {
					t.Fatalf("Failed to execute example in container: %v\nOutput: %s", err, string(output))
				}

				resultsFile := filepath.Join(resultsDir, "results.json")
				if _, err := os.Stat(resultsFile); os.IsNotExist(err) {
					t.Fatalf("Expected results.json not found in output. Container output:\n%s", string(output))
				}
			})
		}
	})
}

// TestPodmanExamplesSync tests the sync functionality using temp directories
func TestPodmanExamplesSync(t *testing.T) {
	// Use temporary directory to avoid modifying actual workspace
	tempDir := t.TempDir()
	tempWorkspace := filepath.Join(tempDir, "workspace")

	// Use fixtures for examples directory operations
	examplesDir := tests.NewExamplesDir(filepath.Join("..", "fixtures", "examples"))

	// Create temporary workspace
	if err := os.MkdirAll(tempWorkspace, 0750); err != nil {
		t.Fatalf("Failed to create temp workspace: %v", err)
	}

	// Get all example projects using fixtures
	projects, err := examplesDir.ListProjects()
	if err != nil {
		t.Fatalf("Failed to read examples directory: %v", err)
	}

	for _, projectName := range projects {
		dstDir := filepath.Join(tempWorkspace, projectName)

		t.Run("Sync_"+projectName, func(t *testing.T) {
			// Remove existing destination
			_ = os.RemoveAll(dstDir)

			// Copy project using fixtures
			if err := examplesDir.CopyProject(projectName, dstDir); err != nil {
				t.Fatalf("Failed to copy %s to test workspace: %v", projectName, err)
			}

			// Verify copy
			requiredFiles := []string{"train.py", "requirements.txt", "README.md"}
			for _, file := range requiredFiles {
				dstFile := filepath.Join(dstDir, file)
				if _, err := os.Stat(dstFile); os.IsNotExist(err) {
					t.Errorf("Missing file %s in copied project %s", file, projectName)
				}
			}

			t.Logf("Successfully synced %s to temp workspace", projectName)
		})
	}
}
