// Package builder provides container image building functionality
package builder

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	git "github.com/go-git/go-git/v5"
)

// Builder handles container image building from source code
type Builder struct {
	config *BuildConfig
}

// BuildConfig defines the configuration for building container images
type BuildConfig struct {
	GitURL       string
	BuilderName  string
	BuildArgs    map[string]*string
	PostBuild    []string
	UseCache     bool
	Timeout      string
	Registry     string // Target registry URL
	RegistryUser string // Registry username
	RegistryPass string // Registry password
	ImageTag     string // Image tag to use
	Namespace    string // Namespace for the image
}

// checkRegistryAvailability checks if the OpenShift internal registry is available
func checkRegistryAvailability(ctx context.Context) bool {
	// Check if the image-registry operator is properly configured
	cmd := exec.CommandContext(ctx, "oc", "get", "configs.imageregistry.operator.openshift.io/cluster", "-o", "jsonpath={.spec.managementState}")
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		log.Printf("Warning: Failed to check registry availability: %v, %s", err, stderr.String())
		return false
	}

	state := strings.TrimSpace(stdout.String())
	if state != "Managed" {
		log.Printf("Warning: OpenShift integrated registry is not available (state: %s)", state)
		return false
	}

	// Check if image-registry pods are running
	podCmd := exec.CommandContext(ctx, "oc", "get", "pods", "-n", "openshift-image-registry", "-l", "docker-registry=default", "-o", "jsonpath={.items[*].status.phase}")
	podCmd.Stdout = &stdout
	podCmd.Stderr = &stderr
	stdout.Reset()
	stderr.Reset()

	if err := podCmd.Run(); err != nil {
		log.Printf("Warning: Failed to check registry pods: %v, %s", err, stderr.String())
		return false
	}

	podStatus := strings.TrimSpace(stdout.String())
	if podStatus == "" || !strings.Contains(podStatus, "Running") {
		log.Printf("Warning: OpenShift integrated registry pods are not running: %s", podStatus)
		return false
	}

	log.Printf("OpenShift integrated registry is available")
	return true
}

// NewBuilder creates a new Builder instance with the given configuration
func NewBuilder(cfg *BuildConfig) (*Builder, error) {
	// Configure logging
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	// Validate configuration
	if cfg == nil {
		return nil, fmt.Errorf("config cannot be nil")
	}

	if cfg.GitURL == "" {
		return nil, fmt.Errorf("GitURL must be specified")
	}

	// Always assume we're running in OpenShift
	log.Printf("Running in OpenShift environment, will use BuildConfig for image building")

	// Check if the OpenShift registry is available
	registryAvailable := checkRegistryAvailability(context.Background())
	if !registryAvailable {
		return nil, fmt.Errorf("OpenShift integrated registry is not available. Please ensure the image registry operator is configured (managementState: Managed)")
	}

	// Set defaults if not provided
	if cfg.Registry == "" {
		cfg.Registry = "image-registry.openshift-image-registry.svc:5000"
	}

	if cfg.ImageTag == "" {
		cfg.ImageTag = "latest"
	}

	// Determine builder type if not explicitly specified
	if cfg.BuilderName == "" {
		// Since we don't have access to the repository yet, we can't auto-detect
		// Default to static HTML for safety
		log.Printf("No builder specified, defaulting to static-html")
		cfg.BuilderName = "static-html"
	}

	// Create Builder instance
	builder := &Builder{
		config: cfg,
	}

	return builder, nil
}

// Build builds a container image from source code and pushes it to the registry
func (b *Builder) Build(ctx context.Context) (string, error) {
	// Extract repository name for the image name
	repoName := b.getRepoName()

	// Create the full image name with registry
	imageName := b.getFullImageName(repoName)

	// Check if there's an active build already running for this resource
	activeBuildsCmd := exec.CommandContext(ctx, "oc", "get", "builds", "-n", b.config.Namespace, "-l", "buildconfig="+repoName, "--field-selector=status=Running,status=Pending,status=New", "-o", "name")
	var activeBuildsOutput bytes.Buffer
	activeBuildsCmd.Stdout = &activeBuildsOutput
	activeBuildsCmd.Stderr = io.Discard

	// Check for active builds
	if err := activeBuildsCmd.Run(); err == nil && activeBuildsOutput.String() != "" {
		// There are existing active builds, don't start another one
		log.Printf("Found active builds for %s: %s - will not start another build", repoName, activeBuildsOutput.String())
		return imageName, nil
	}

	// For builds with valid Git URL, try using Git source directly first
	if b.config.GitURL != "" && b.config.GitURL != "placeholder" {
		log.Printf("Using Git URL directly for build: %s", b.config.GitURL)

		// Clone repository just for detecting the appropriate builder
		tempDir, err := b.cloneRepo(ctx)
		if err != nil {
			return "", fmt.Errorf("failed to clone repo for detection: %w", err)
		}
		defer func() {
			if err := os.RemoveAll(tempDir); err != nil {
				log.Printf("failed to remove temp dir: %v", err)
			}
		}()

		// Detect language and framework
		builder, err := b.detectBuilder(tempDir)
		if err != nil {
			// If detection fails but this is a static HTML site request, create a default builder
			if b.config.BuilderName == "static-html" {
				log.Printf("Detection failed but static HTML was requested, using default static HTML builder")
				staticBuilder, err := staticHTMLBuilder(b.config.BuilderName)
				if err != nil {
					return "", fmt.Errorf("failed to create static HTML builder: %w", err)
				}
				builder = staticBuilder
			} else {
				return "", fmt.Errorf("failed to detect builder: %w", err)
			}
		}

		// Try to create a BuildConfig that uses Git source directly
		err = createOpenShiftGitBuildConfig(ctx, b.config.Namespace, repoName, b.config.GitURL, builder)
		if err != nil {
			// If Git source build fails, fall back to local source
			log.Printf("Git source build failed, falling back to local source: %v", err)

			// Create a temporary BuildConfig for this build using local source
			if err := createOpenShiftBuildConfig(ctx, b.config.Namespace, repoName, tempDir, builder); err != nil {
				// For static HTML sites, use fallback image if build fails
				if strings.Contains(builder.builderImage, "httpd") || strings.Contains(builder.builderImage, "nginx") {
					log.Printf("BuildConfig creation failed for static HTML site, using fallback image")
					return "quay.io/nginx/nginx-unprivileged:latest", nil
				}
				return "", fmt.Errorf("failed to create OpenShift build config: %w", err)
			}
		}

		return imageName, nil
	}

	// If no Git URL is available, proceed with local source build

	// Clone repository
	tempDir, err := b.cloneRepo(ctx)
	if err != nil {
		return "", fmt.Errorf("failed to clone repo: %w", err)
	}
	defer func() {
		if err := os.RemoveAll(tempDir); err != nil {
			log.Printf("failed to remove temp dir: %v", err)
		}
	}()

	// Detect language and framework
	builder, err := b.detectBuilder(tempDir)
	if err != nil {
		// If detection fails but this is a static HTML site request, create a default builder
		if b.config.BuilderName == "static-html" {
			log.Printf("Detection failed but static HTML was requested, using default static HTML builder")
			staticBuilder, err := staticHTMLBuilder(b.config.BuilderName)
			if err != nil {
				return "", fmt.Errorf("failed to create static HTML builder: %w", err)
			}
			builder = staticBuilder
		} else {
			return "", fmt.Errorf("failed to detect builder: %w", err)
		}
	}

	// Always use OpenShift BuildConfig for image building
	log.Printf("Using OpenShift BuildConfig for image building")

	// Create a temporary BuildConfig for this build
	if err := createOpenShiftBuildConfig(ctx, b.config.Namespace, repoName, tempDir, builder); err != nil {
		// For static HTML sites, use fallback image if build fails
		if strings.Contains(builder.builderImage, "httpd") || strings.Contains(builder.builderImage, "nginx") {
			log.Printf("BuildConfig creation failed for static HTML site, using fallback image")
			return "quay.io/nginx/nginx-unprivileged:latest", nil
		}
		return "", fmt.Errorf("failed to create OpenShift build config: %w", err)
	}

	return imageName, nil
}

// createOpenShiftBuildConfig creates an OpenShift BuildConfig for building the image
func createOpenShiftBuildConfig(ctx context.Context, namespace, name, sourcePath string, builder *LanguageBuilder) error {
	// Use the OpenShiftBuilder to create the build config
	log.Printf("Creating OpenShift BuildConfig for %s in namespace %s", name, namespace)

	// For static HTML sites, use a nginx-unprivileged which is more compatible with OpenShift
	useOpenShiftCompatibleImage := strings.Contains(builder.builderImage, "httpd") || strings.Contains(builder.builderImage, "nginx")

	// Determine what Dockerfile content to use
	var dockerfileContent string
	if useOpenShiftCompatibleImage {
		// Use OpenShift-compatible nginx-unprivileged image for static HTML content
		dockerfileContent = `FROM quay.io/nginx/nginx-unprivileged:latest

# Copy content to nginx server
COPY . /usr/share/nginx/html/

# Expose port 8080 (the port nginx-unprivileged listens on)
EXPOSE 8080

# Run nginx in the foreground
CMD ["nginx", "-g", "daemon off;"]
`
		log.Printf("Created OpenShift-compatible Dockerfile for static HTML content using nginx-unprivileged")
	} else {
		// For other types, use the default Dockerfile from the language builder
		dockerfileContent = builder.Dockerfile()
	}

	// Create a minimal config just for this build
	buildConfig := &BuildConfig{
		GitURL:    "", // Not needed for this operation
		Namespace: namespace,
		Registry:  "image-registry.openshift-image-registry.svc:5000",
		ImageTag:  "latest",
	}

	// Create an OpenShift build helper
	helper := NewOpenShiftBuildHelper(buildConfig)

	// Delete existing BuildConfig and ImageStream if they exist
	if err := helper.deleteExistingResources(ctx, namespace, name); err != nil {
		log.Printf("Warning: Could not clean up existing resources: %v", err)
		// Continue anyway, the new build might still work
	}

	// Create or ensure a builder service account exists with proper permissions
	log.Printf("Setting up builder service account in namespace %s", namespace)

	// Create a builder service account if it doesn't exist
	saCmd := exec.CommandContext(ctx, "oc", "get", "serviceaccount", "builder", "-n", namespace)
	saCmd.Stderr = io.Discard
	saCmd.Stdout = io.Discard

	if err := saCmd.Run(); err != nil {
		// Service account doesn't exist, create it
		createSaCmd := exec.CommandContext(ctx, "oc", "create", "serviceaccount", "builder", "-n", namespace)
		createSaCmd.Stdout = os.Stdout
		createSaCmd.Stderr = os.Stderr
		if err := createSaCmd.Run(); err != nil {
			log.Printf("Warning: Failed to create builder service account: %v", err)
			// Continue anyway, it might already exist in a different form
		}
	}

	// Add the builder service account to system:image-builder role
	roleCmd := exec.CommandContext(ctx, "oc", "policy", "add-role-to-user", "system:image-builder", "-z", "builder", "-n", namespace)
	roleCmd.Stdout = os.Stdout
	roleCmd.Stderr = os.Stderr
	if err := roleCmd.Run(); err != nil {
		log.Printf("Warning: Failed to add system:image-builder role to builder service account: %v", err)
		// Continue anyway, the role might already be assigned
	}

	// Add the builder service account to edit role for managing builds
	editRoleCmd := exec.CommandContext(ctx, "oc", "policy", "add-role-to-user", "edit", "-z", "builder", "-n", namespace)
	editRoleCmd.Stdout = os.Stdout
	editRoleCmd.Stderr = os.Stderr
	if err := editRoleCmd.Run(); err != nil {
		log.Printf("Warning: Failed to add edit role to builder service account: %v", err)
		// Continue anyway, the role might already be assigned
	}

	// Check if the builder service account has a token
	tokenCmd := exec.CommandContext(ctx, "oc", "get", "secret", "-n", namespace, "--field-selector=type=kubernetes.io/service-account-token", "-o", "jsonpath={.items[?(@.metadata.annotations.kubernetes\\.io/service-account\\.name==\"builder\")].metadata.name}")
	var tokenOutput bytes.Buffer
	tokenCmd.Stdout = &tokenOutput
	tokenCmd.Stderr = os.Stderr

	if err := tokenCmd.Run(); err != nil || tokenOutput.String() == "" {
		// No token found, create one
		log.Printf("No token found for builder service account, creating one")

		// Create a token secret using the oc command
		// First, create a unique token name
		tokenName := fmt.Sprintf("builder-token-%d", time.Now().Unix())

		// Generate a token secret YAML
		tokenYaml := fmt.Sprintf(`
apiVersion: v1
kind: Secret
metadata:
  name: %s
  namespace: %s
  annotations:
    kubernetes.io/service-account.name: builder
type: kubernetes.io/service-account-token
`, tokenName, namespace)

		// Write the token YAML to a temporary file
		tokenTempFile, err := os.CreateTemp("", "token-*.yaml")
		if err != nil {
			log.Printf("Warning: Failed to create temporary file for token secret: %v", err)
		} else {
			defer os.Remove(tokenTempFile.Name())

			if _, err := tokenTempFile.WriteString(tokenYaml); err != nil {
				log.Printf("Warning: Failed to write token YAML: %v", err)
			} else {
				tokenTempFile.Close()

				// Apply the token secret
				tokenCreateCmd := exec.CommandContext(ctx, "oc", "apply", "-f", tokenTempFile.Name())
				tokenCreateCmd.Stdout = os.Stdout
				tokenCreateCmd.Stderr = os.Stderr

				if err := tokenCreateCmd.Run(); err != nil {
					log.Printf("Warning: Failed to create token secret: %v", err)
				} else {
					log.Printf("Created token secret %s for builder service account", tokenName)

					// Give token some time to be processed
					time.Sleep(2 * time.Second)
				}
			}
		}
	} else {
		log.Printf("Found existing token for builder service account: %s", tokenOutput.String())
	}

	// Create an explicit ImageStream with a properly defined repository
	// This is critical for the BuildConfig to resolve the output image
	isYaml := fmt.Sprintf(`
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  name: %s
  namespace: %s
spec:
  lookupPolicy:
    local: true
  dockerImageRepository: %s/%s/%s
`, name, namespace, buildConfig.Registry, namespace, name)

	// Write the ImageStream YAML to a temporary file
	isTempFile, err := os.CreateTemp("", "imagestream-*.yaml")
	if err != nil {
		return fmt.Errorf("failed to create temporary file for ImageStream: %w", err)
	}
	defer os.Remove(isTempFile.Name())

	if _, err := isTempFile.WriteString(isYaml); err != nil {
		return fmt.Errorf("failed to write ImageStream YAML: %w", err)
	}
	isTempFile.Close()

	// Apply the ImageStream
	isCmd := exec.CommandContext(ctx, "oc", "apply", "-f", isTempFile.Name())
	isCmd.Stdout = os.Stdout
	isCmd.Stderr = os.Stderr

	if err := isCmd.Run(); err != nil {
		return fmt.Errorf("failed to apply ImageStream: %w", err)
	}
	log.Printf("Created ImageStream %s in namespace %s with repository %s/%s/%s",
		name, namespace, buildConfig.Registry, namespace, name)

	// Try to find Git repository URL from the parent BuildConfig
	// Read the Git URL from a file in the source directory
	gitInfoPath := filepath.Join(sourcePath, ".git/config")
	gitURL := ""

	if _, err := os.Stat(gitInfoPath); err == nil {
		// Git config exists, try to extract the remote URL
		gitConfigBytes, err := os.ReadFile(gitInfoPath)
		if err == nil {
			gitConfig := string(gitConfigBytes)
			// Simple regex to extract URL from git config
			urlRegex := regexp.MustCompile(`url = (.+)`)
			matches := urlRegex.FindStringSubmatch(gitConfig)
			if len(matches) > 1 {
				gitURL = strings.TrimSpace(matches[1])
				log.Printf("Found Git URL from repo: %s", gitURL)
			}
		}
	}

	// If we have a Git URL, create a BuildConfig with Git source for any project type
	if gitURL != "" {
		// Create a YAML file for the BuildConfig
		bcYaml := fmt.Sprintf(`
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
  name: %s
  namespace: %s
spec:
  serviceAccount: builder
  source:
    git:
      uri: "%s"
      ref: "main"
    dockerfile: |
%s
  strategy:
    type: Docker
    dockerStrategy:
      noCache: false
  output:
    to:
      kind: ImageStreamTag
      name: %s:latest
  triggers:
    - type: ConfigChange
`, name, namespace, gitURL, addIndentation(dockerfileContent, 6), name)

		// Write the BuildConfig YAML to a temporary file
		tempFile, err := os.CreateTemp("", "buildconfig-*.yaml")
		if err != nil {
			return fmt.Errorf("failed to create temporary file for BuildConfig: %w", err)
		}
		defer os.Remove(tempFile.Name())

		if _, err := tempFile.WriteString(bcYaml); err != nil {
			return fmt.Errorf("failed to write BuildConfig YAML: %w", err)
		}
		tempFile.Close()

		// Apply the BuildConfig
		cmd := exec.CommandContext(ctx, "oc", "apply", "-f", tempFile.Name())
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			return fmt.Errorf("failed to apply BuildConfig: %w", err)
		}

		// Wait a moment to ensure resources are created properly
		time.Sleep(2 * time.Second)

		// Start the build
		startCmd := exec.CommandContext(ctx, "oc", "start-build", name, "-n", namespace, "--follow")
		startCmd.Stdout = os.Stdout
		startCmd.Stderr = os.Stderr

		if err := startCmd.Run(); err != nil {
			log.Printf("Build failed, but will continue with deployment: %v", err)
			// For static HTML sites, use fallback image if build fails
			if useOpenShiftCompatibleImage {
				log.Printf("Using fallback image for static HTML site")
				return nil
			}
			return fmt.Errorf("build failed: %w", err)
		}

		return nil
	}

	// Fallback to binary build if no Git URL was found
	log.Printf("No Git URL found, using binary build strategy")

	// Use YAML approach for binary build configuration too
	bcYaml := fmt.Sprintf(`
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
  name: %s
  namespace: %s
spec:
  serviceAccount: builder
  source:
    type: Binary
    dockerfile: |
%s
  strategy:
    type: Docker
    dockerStrategy:
      noCache: false
  output:
    to:
      kind: ImageStreamTag
      name: %s:latest
  triggers:
    - type: ConfigChange
`, name, namespace, addIndentation(dockerfileContent, 6), name)

	// Write the BuildConfig YAML to a temporary file
	tempFile, err := os.CreateTemp("", "buildconfig-*.yaml")
	if err != nil {
		return fmt.Errorf("failed to create temporary file for BuildConfig: %w", err)
	}
	defer os.Remove(tempFile.Name())

	if _, err := tempFile.WriteString(bcYaml); err != nil {
		return fmt.Errorf("failed to write BuildConfig YAML: %w", err)
	}
	tempFile.Close()

	// Apply the BuildConfig
	cmd := exec.CommandContext(ctx, "oc", "apply", "-f", tempFile.Name())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to apply BuildConfig: %w", err)
	}

	// Wait a moment to ensure resources are created properly
	time.Sleep(2 * time.Second)

	// Start the build with the binary content
	startCmd := exec.CommandContext(ctx, "oc", "start-build", name,
		"--from-dir="+sourcePath,
		"-n", namespace,
		"--follow")
	startCmd.Stdout = os.Stdout
	startCmd.Stderr = os.Stderr

	if err := startCmd.Run(); err != nil {
		log.Printf("Build failed, but will continue with deployment: %v", err)
		// For static HTML sites, use fallback image if build fails
		if useOpenShiftCompatibleImage {
			log.Printf("Using fallback image for static HTML site")
			return nil
		}
		return fmt.Errorf("build failed: %w", err)
	}

	return nil
}

// createOpenShiftGitBuildConfig creates an OpenShift BuildConfig directly from a Git repository
func createOpenShiftGitBuildConfig(ctx context.Context, namespace, name, gitURL string, builder *LanguageBuilder) error {
	// Use the OpenShiftBuilder to create the build config
	log.Printf("Creating OpenShift BuildConfig for %s in namespace %s using Git source", name, namespace)

	// For static HTML sites, use nginx-unprivileged which is more compatible with OpenShift
	useOpenShiftCompatibleImage := strings.Contains(builder.builderImage, "httpd") || strings.Contains(builder.builderImage, "nginx")

	// Determine what Dockerfile content to use
	var dockerfileContent string
	if useOpenShiftCompatibleImage {
		// Use OpenShift-compatible nginx-unprivileged image for static HTML content
		dockerfileContent = `FROM quay.io/nginx/nginx-unprivileged:latest

# Copy content to nginx server
COPY . /usr/share/nginx/html/

# Expose port 8080 (the port nginx-unprivileged listens on)
EXPOSE 8080

# Run nginx in the foreground
CMD ["nginx", "-g", "daemon off;"]
`
		log.Printf("Using nginx-unprivileged Dockerfile for static HTML content")
	} else {
		// For other types, use the default Dockerfile from the language builder
		dockerfileContent = builder.Dockerfile()
	}

	// Create a minimal config just for this build
	buildConfig := &BuildConfig{
		GitURL:    gitURL,
		Namespace: namespace,
		Registry:  "image-registry.openshift-image-registry.svc:5000",
		ImageTag:  "latest",
	}

	// Create an OpenShift build helper
	helper := NewOpenShiftBuildHelper(buildConfig)

	// Delete existing BuildConfig and ImageStream if they exist
	if err := helper.deleteExistingResources(ctx, namespace, name); err != nil {
		log.Printf("Warning: Could not clean up existing resources: %v", err)
		// Continue anyway, the new build might still work
	}

	// Create or ensure a builder service account exists with proper permissions
	log.Printf("Setting up builder service account in namespace %s", namespace)

	// Create a builder service account if it doesn't exist
	saCmd := exec.CommandContext(ctx, "oc", "get", "serviceaccount", "builder", "-n", namespace)
	saCmd.Stderr = io.Discard
	saCmd.Stdout = io.Discard

	if err := saCmd.Run(); err != nil {
		// Service account doesn't exist, create it
		createSaCmd := exec.CommandContext(ctx, "oc", "create", "serviceaccount", "builder", "-n", namespace)
		createSaCmd.Stdout = os.Stdout
		createSaCmd.Stderr = os.Stderr
		if err := createSaCmd.Run(); err != nil {
			log.Printf("Warning: Failed to create builder service account: %v", err)
			// Continue anyway, it might already exist in a different form
		}
	}

	// Add the builder service account to system:image-builder role
	roleCmd := exec.CommandContext(ctx, "oc", "policy", "add-role-to-user", "system:image-builder", "-z", "builder", "-n", namespace)
	roleCmd.Stdout = os.Stdout
	roleCmd.Stderr = os.Stderr
	if err := roleCmd.Run(); err != nil {
		log.Printf("Warning: Failed to add system:image-builder role to builder service account: %v", err)
		// Continue anyway, the role might already be assigned
	}

	// Add the builder service account to edit role for managing builds
	editRoleCmd := exec.CommandContext(ctx, "oc", "policy", "add-role-to-user", "edit", "-z", "builder", "-n", namespace)
	editRoleCmd.Stdout = os.Stdout
	editRoleCmd.Stderr = os.Stderr
	if err := editRoleCmd.Run(); err != nil {
		log.Printf("Warning: Failed to add edit role to builder service account: %v", err)
		// Continue anyway, the role might already be assigned
	}

	// Check if the builder service account has a token
	tokenCmd := exec.CommandContext(ctx, "oc", "get", "secret", "-n", namespace, "--field-selector=type=kubernetes.io/service-account-token", "-o", "jsonpath={.items[?(@.metadata.annotations.kubernetes\\.io/service-account\\.name==\"builder\")].metadata.name}")
	var tokenOutput bytes.Buffer
	tokenCmd.Stdout = &tokenOutput
	tokenCmd.Stderr = os.Stderr

	if err := tokenCmd.Run(); err != nil || tokenOutput.String() == "" {
		// No token found, create one
		log.Printf("No token found for builder service account, creating one")

		// Create a token secret using the oc command
		// First, create a unique token name
		tokenName := fmt.Sprintf("builder-token-%d", time.Now().Unix())

		// Generate a token secret YAML
		tokenYaml := fmt.Sprintf(`
apiVersion: v1
kind: Secret
metadata:
  name: %s
  namespace: %s
  annotations:
    kubernetes.io/service-account.name: builder
type: kubernetes.io/service-account-token
`, tokenName, namespace)

		// Write the token YAML to a temporary file
		tokenTempFile, err := os.CreateTemp("", "token-*.yaml")
		if err != nil {
			log.Printf("Warning: Failed to create temporary file for token secret: %v", err)
		} else {
			defer os.Remove(tokenTempFile.Name())

			if _, err := tokenTempFile.WriteString(tokenYaml); err != nil {
				log.Printf("Warning: Failed to write token YAML: %v", err)
			} else {
				tokenTempFile.Close()

				// Apply the token secret
				tokenCreateCmd := exec.CommandContext(ctx, "oc", "apply", "-f", tokenTempFile.Name())
				tokenCreateCmd.Stdout = os.Stdout
				tokenCreateCmd.Stderr = os.Stderr

				if err := tokenCreateCmd.Run(); err != nil {
					log.Printf("Warning: Failed to create token secret: %v", err)
				} else {
					log.Printf("Created token secret %s for builder service account", tokenName)

					// Give token some time to be processed
					time.Sleep(2 * time.Second)
				}
			}
		}
	} else {
		log.Printf("Found existing token for builder service account: %s", tokenOutput.String())
	}

	// Create an explicit ImageStream with a properly defined repository
	// This is critical for the BuildConfig to resolve the output image
	isYaml := fmt.Sprintf(`
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  name: %s
  namespace: %s
spec:
  lookupPolicy:
    local: true
  dockerImageRepository: %s/%s/%s
`, name, namespace, buildConfig.Registry, namespace, name)

	// Write the ImageStream YAML to a temporary file
	isTempFile, err := os.CreateTemp("", "imagestream-*.yaml")
	if err != nil {
		return fmt.Errorf("failed to create temporary file for ImageStream: %w", err)
	}
	defer os.Remove(isTempFile.Name())

	if _, err := isTempFile.WriteString(isYaml); err != nil {
		return fmt.Errorf("failed to write ImageStream YAML: %w", err)
	}
	isTempFile.Close()

	// Apply the ImageStream
	isCmd := exec.CommandContext(ctx, "oc", "apply", "-f", isTempFile.Name())
	isCmd.Stdout = os.Stdout
	isCmd.Stderr = os.Stderr

	if err := isCmd.Run(); err != nil {
		return fmt.Errorf("failed to apply ImageStream: %w", err)
	}
	log.Printf("Created ImageStream %s in namespace %s with repository %s/%s/%s",
		name, namespace, buildConfig.Registry, namespace, name)

	// Create a YAML file for the BuildConfig with Git source
	bcYaml := fmt.Sprintf(`
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
  name: %s
  namespace: %s
spec:
  serviceAccount: builder
  source:
    git:
      uri: "%s"
      ref: "main"
    dockerfile: |
%s
  strategy:
    type: Docker
    dockerStrategy:
      noCache: false
  output:
    to:
      kind: ImageStreamTag
      name: %s:latest
  triggers:
    - type: ConfigChange
`, name, namespace, gitURL, addIndentation(dockerfileContent, 6), name)

	// Write the BuildConfig YAML to a temporary file
	tempFile, err := os.CreateTemp("", "buildconfig-*.yaml")
	if err != nil {
		return fmt.Errorf("failed to create temporary file for BuildConfig: %w", err)
	}
	defer os.Remove(tempFile.Name())

	if _, err := tempFile.WriteString(bcYaml); err != nil {
		return fmt.Errorf("failed to write BuildConfig YAML: %w", err)
	}
	tempFile.Close()

	// Apply the BuildConfig
	cmd := exec.CommandContext(ctx, "oc", "apply", "-f", tempFile.Name())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to apply BuildConfig: %w", err)
	}

	// Wait a moment to ensure resources are created properly
	time.Sleep(2 * time.Second)

	// Start the build
	startCmd := exec.CommandContext(ctx, "oc", "start-build", name, "-n", namespace, "--follow")
	startCmd.Stdout = os.Stdout
	startCmd.Stderr = os.Stderr

	if err := startCmd.Run(); err != nil {
		log.Printf("Build failed, but will continue with deployment: %v", err)
		// For static HTML sites, use fallback image if build fails
		if useOpenShiftCompatibleImage {
			log.Printf("Using fallback image for static HTML site")
			return nil
		}
		return fmt.Errorf("build failed: %w", err)
	}

	return nil
}

// addIndentation adds indentation to each line of a multi-line string
func addIndentation(s string, spaces int) string {
	indent := strings.Repeat(" ", spaces)
	lines := strings.Split(s, "\n")
	for i, line := range lines {
		if line != "" {
			lines[i] = indent + line
		}
	}
	return strings.Join(lines, "\n")
}

// getRepoName extracts the repository name from the Git URL
func (b *Builder) getRepoName() string {
	parts := strings.Split(b.config.GitURL, "/")
	if len(parts) == 0 {
		return "app"
	}

	// Get the last part and remove .git suffix if present
	repoName := parts[len(parts)-1]
	repoName = strings.TrimSuffix(repoName, ".git")

	// Replace invalid characters
	reg := strings.NewReplacer(
		" ", "-",
		"_", "-",
		".", "-",
		":", "-",
		"/", "-",
	)
	return strings.ToLower(reg.Replace(repoName))
}

// getFullImageName constructs the full image name with registry and namespace
func (b *Builder) getFullImageName(repoName string) string {
	namespace := b.config.Namespace
	if namespace == "" {
		namespace = "default"
	}

	return fmt.Sprintf("%s/%s/%s:%s",
		b.config.Registry,
		namespace,
		repoName,
		b.config.ImageTag)
}

func (b *Builder) cloneRepo(ctx context.Context) (string, error) {
	tempDir, err := os.MkdirTemp("", "build-*")
	if err != nil {
		return "", err
	}

	_, err = git.PlainCloneContext(ctx, tempDir, false, &git.CloneOptions{
		URL:      b.config.GitURL,
		Progress: os.Stdout,
	})
	if err != nil {
		if err := os.RemoveAll(tempDir); err != nil {
			return "", fmt.Errorf("failed to remove temp directory: %w", err)
		}
		return "", err
	}

	return tempDir, nil
}

func (b *Builder) detectBuilder(repoPath string) (*LanguageBuilder, error) {
	// Check for package.json
	if _, err := os.Stat(filepath.Join(repoPath, "package.json")); err == nil {
		return nodeJSBuilder(b.config.BuilderName)
	}

	// Check for requirements.txt or pyproject.toml
	if _, err := os.Stat(filepath.Join(repoPath, "requirements.txt")); err == nil {
		return pythonBuilder(b.config.BuilderName)
	}

	if _, err := os.Stat(filepath.Join(repoPath, "pyproject.toml")); err == nil {
		return pythonBuilder(b.config.BuilderName)
	}

	// Check for go.mod
	if _, err := os.Stat(filepath.Join(repoPath, "go.mod")); err == nil {
		return goBuilder(b.config.BuilderName)
	}

	// Enhanced HTML detection
	// First, check for common HTML file patterns
	commonHtmlFiles := []string{
		"index.html",
		"default.html",
		"home.html",
		"main.html",
	}

	for _, htmlFile := range commonHtmlFiles {
		if _, err := os.Stat(filepath.Join(repoPath, htmlFile)); err == nil {
			log.Printf("Detected static HTML site with %s file", htmlFile)
			return staticHTMLBuilder(b.config.BuilderName)
		}
	}

	// Try to find any HTML files in the root directory
	files, err := os.ReadDir(repoPath)
	if err != nil {
		return nil, fmt.Errorf("unable to read repository directory: %w", err)
	}

	// Count how many HTML and CSS files we have to determine if this is likely a static site
	htmlCount := 0
	cssCount := 0
	jsCount := 0
	imageCount := 0

	for _, file := range files {
		if file.IsDir() {
			// Check if this is a common static site directory pattern
			if file.Name() == "static" || file.Name() == "assets" ||
				file.Name() == "css" || file.Name() == "js" ||
				file.Name() == "images" || file.Name() == "img" {
				log.Printf("Detected static HTML site with %s directory", file.Name())
				return staticHTMLBuilder(b.config.BuilderName)
			}
			continue
		}

		// Count file types by extension
		filename := strings.ToLower(file.Name())
		if strings.HasSuffix(filename, ".html") || strings.HasSuffix(filename, ".htm") {
			htmlCount++
		} else if strings.HasSuffix(filename, ".css") {
			cssCount++
		} else if strings.HasSuffix(filename, ".js") {
			jsCount++
		} else if strings.HasSuffix(filename, ".jpg") || strings.HasSuffix(filename, ".jpeg") ||
			strings.HasSuffix(filename, ".png") || strings.HasSuffix(filename, ".gif") ||
			strings.HasSuffix(filename, ".svg") || strings.HasSuffix(filename, ".webp") {
			imageCount++
		}
	}

	// If we have any HTML files, or a combination of CSS/JS/images that suggests a static site
	if htmlCount > 0 || (cssCount > 0 && jsCount > 0) || (cssCount+jsCount+imageCount >= 3) {
		log.Printf("Detected likely static HTML site: %d HTML, %d CSS, %d JS, %d image files",
			htmlCount, cssCount, jsCount, imageCount)
		return staticHTMLBuilder(b.config.BuilderName)
	}

	// As a last resort, check if there's any content at all
	// If there's any file that could be served as static content, use httpd
	if len(files) > 0 {
		// Count how many files that aren't hidden or system files
		visibleFiles := 0
		for _, file := range files {
			if !file.IsDir() && !strings.HasPrefix(file.Name(), ".") {
				visibleFiles++
			}
		}

		// If we have a few visible files but couldn't detect a specific language,
		// default to static HTML hosting
		if visibleFiles > 0 {
			log.Printf("No specific language detected but found %d files, defaulting to static HTML", visibleFiles)
			return staticHTMLBuilder(b.config.BuilderName)
		}
	}

	return nil, fmt.Errorf("no supported language detected")
}
