package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/niveus/cloud-build-go/cloudbuild"
	"github.com/niveus/cloud-build-go/config"
	cloudbuildv2 "google.golang.org/api/cloudbuild/v2"
)

// printRepositoryInfo prints debug information about a repository
func printRepositoryInfo(repo *cloudbuildv2.Repository) {
	fmt.Println("Repository Information:")
	fmt.Printf("  Name: %s\n", repo.Name)
	fmt.Printf("  Remote URI: %s\n", repo.RemoteUri)
	fmt.Printf("  Create Time: %s\n", repo.CreateTime)
	fmt.Printf("  Update Time: %s\n", repo.UpdateTime)
	
	fmt.Println("  Annotations:")
	for k, v := range repo.Annotations {
		fmt.Printf("    %s: %s\n", k, v)
	}
}

func main() {
	// Define command-line flags
	configPath := flag.String("config", "config/config.json", "Path to config file")
	action := flag.String("action", "list-connections", "Action to perform: list-connections, create-gitlab, delete-connection, list-linkable-repos, inspect")
	connectionName := flag.String("name", "", "Name for the connection")
	flag.Parse()

	// Project ID, location, connection type will come from config file
	var projectIDValue, locationValue, connTypeValue string
	
	// Load config file
	cfg, err := config.LoadConfig(*configPath)
	if err != nil {
		log.Fatalf("Failed to load config file: %v", err)
	}
	// Set values from config file
	projectIDValue = cfg.ProjectID
	locationValue = cfg.Location
	connTypeValue = cfg.DefaultConnType
	
	// Log the values that will be used
	log.Printf("Config values set to: Project=%s, Location=%s, Type=%s", 
		projectIDValue, locationValue, connTypeValue)
	
	// Check for required values after loading from config
	if projectIDValue == "" {
		log.Fatal("project ID is required in the config file")
	}

	// We no longer need this validation because each case will handle missing connection names by
	// automatically using values from the config file
	// Validation still happens in each case for more specific handling

	ctx := context.Background()

	// Create a Cloud Build client using our package
	var client *cloudbuild.Client
	
	// Authentication approach Application Default Credentials
	client, err = cloudbuild.NewClient(ctx)
	if err != nil {
		log.Fatalf("Failed to create Cloud Build client: %v", err)
	}

	// Perform the requested action
	switch *action {

	case "create-gitlab":
		// Find the connection config for the given name
		var connConfig *config.ConnectionConfig
		for _, conn := range cfg.Connections {
			if conn.Name == *connectionName || *connectionName == "" && len(cfg.Connections) == 1 {
				connConfig = &conn
				break
			}
		}
		
		if connConfig == nil {
			if *connectionName == "" {
				log.Fatalf("No connection configuration found in config file. Please add connections to your config file.")
			} else {
				log.Fatalf("No connection configuration found for name '%s' in config file", *connectionName)
			}
		}
		
		// Check if the connection config has GitLab configuration
		if connConfig.GitlabConfig == nil {
			log.Fatalf("Connection '%s' does not have GitLab configuration", connConfig.Name)
		}
		
		log.Printf("Creating GitLab connection using config: ProjectID=%s, Location=%s, Name=%s", 
			projectIDValue, locationValue, connConfig.Name)
		
		fmt.Printf("Creating GitLab connection '%s' in project '%s' (location: '%s')\n", 
			connConfig.Name, projectIDValue, locationValue)
		
		// Create the GitLab connection using the Cloud Build SDK
		err, resourceName := client.CreateGitLabConnection(
			projectIDValue, 
			locationValue, 
			connConfig.Name, 
			connConfig.GitlabConfig,
			connConfig.Annotations,
		)
		if err != nil {
			log.Fatalf("Failed to create GitLab connection: %v", err)
		}
		
		fmt.Printf("Connection creation initiated successfully. GitLab connection resource: %s\n", resourceName)
		fmt.Println("Note: The connection might take a moment to become fully active in the Cloud Build system.")

	case "list-connections":
		// Use project ID and location from config
		log.Printf("Listing connections in project %s (location: %s)", projectIDValue, locationValue)
		
		connections, err := client.ListConnections(projectIDValue, locationValue)
		if err != nil {
			log.Fatalf("Failed to list connections: %v", err)
		}
		fmt.Printf("Found %d connections in project %s (location: %s):\n", 
			len(connections), projectIDValue, locationValue)
		for i, conn := range connections {
			// Determine connection type based on config
			connType := "Unknown"
			if conn.GithubConfig != nil {
				connType = "GitHub"
			} else if conn.GitlabConfig != nil {
				connType = "GitLab"
			}
			fmt.Printf("%d. %s (Type: %s)\n", i+1, conn.Name, connType)
		}



	case "list-linkable-repos":
		// If connection name not provided via command line, get it from config
		connNameToList := *connectionName
		if connNameToList == "" {
			// If there's only one connection in config, use that
			if len(cfg.Connections) == 1 {
				connNameToList = cfg.Connections[0].Name
				fmt.Printf("Using connection name from config: %s\n", connNameToList)
			} else if len(cfg.Connections) > 1 {
				// If multiple connections, show the available options
				fmt.Println("Multiple connections found in config. Please specify which one to use with --name flag:")
				for i, conn := range cfg.Connections {
					fmt.Printf("%d. %s\n", i+1, conn.Name)
				}
				os.Exit(1)
			} else {
				log.Fatalf("No connections found in config file")
			}
		}
		
		log.Printf("Listing linkable repositories for connection %s in project %s (location: %s)", 
			connNameToList, projectIDValue, locationValue)
		
		repositories, err := client.ListLinkableRepositories(projectIDValue, locationValue, connNameToList)
		if err != nil {
			log.Fatalf("Failed to list linkable repositories: %v", err)
		}
		fmt.Printf("Found %d linkable repositories for connection %s:\n", 
			len(repositories), connNameToList)
		for i, repo := range repositories {
			fmt.Printf("%d. %s\n", i+1, repo.Name)
			if repo.RemoteUri != "" {
				fmt.Printf("   URI: %s\n", repo.RemoteUri)
			}
			// Add a blank line between repositories for better readability
			if i < len(repositories)-1 {
				fmt.Println()
			}
		}
		
	case "link-repository":
		// If connection name not provided via command line, get it from config
		connNameToLink := *connectionName
		if connNameToLink == "" {
			// If there's only one connection in config, use that
			if len(cfg.Connections) == 1 {
				connNameToLink = cfg.Connections[0].Name
				fmt.Printf("Using connection name from config: %s\n", connNameToLink)
			} else if len(cfg.Connections) > 1 {
				// If multiple connections, show the available options
				fmt.Println("Multiple connections found in config. Please specify which one to use with --name flag:")
				for i, conn := range cfg.Connections {
					fmt.Printf("%d. %s\n", i+1, conn.Name)
				}
				os.Exit(1)
			} else {
				log.Fatalf("No connections found in config file")
			}
		}
		
		// Get the linkable repositories for this connection
		log.Printf("Finding linkable repositories for connection %s in project %s (location: %s)", 
			connNameToLink, projectIDValue, locationValue)
		
		linkableRepos, err := client.ListLinkableRepositories(projectIDValue, locationValue, connNameToLink)
		if err != nil {
			log.Fatalf("Failed to list linkable repositories: %v", err)
		}
		
		if len(linkableRepos) == 0 {
			log.Fatalf("No linkable repositories found for connection %s", connNameToLink)
		}
		
		// Use the first repository in the list
		repoToLink := linkableRepos[0]
		
		// Extract and use a valid repository name
		// First check if the repository has a RemoteUri field which usually contains the repo info
		var repoName string
		if repoToLink.RemoteUri != "" {
			// Extract name from the remote URI (e.g., https://gitlab.com/namespace/repo-name)
			uriParts := strings.Split(repoToLink.RemoteUri, "/")
			if len(uriParts) > 0 {
				repoName = uriParts[len(uriParts)-1]
				// Remove .git suffix if present
				repoName = strings.TrimSuffix(repoName, ".git")
			}
		}
		
		// If we still don't have a name, use attributes (which is a more reliable way)
		if repoName == "" {
			// For GitLab repositories, the name is in the attributes
			printRepositoryInfo(repoToLink) // Print debug info
			
			// Try common attribute keys that might contain the name
			if repoToLink.Annotations != nil {
				if name, ok := repoToLink.Annotations["name"]; ok && name != "" {
					repoName = name
				} else if name, ok := repoToLink.Annotations["repository_name"]; ok && name != "" {
					repoName = name
				}
			}
		}
		
		// If we still don't have a name, ask the user to provide one manually
		if repoName == "" {
			fmt.Println("Unable to automatically determine repository name. Please provide one manually:")
			fmt.Print("Repository name: ")
			fmt.Scanln(&repoName)
		}
		
		// Make sure the repository name is valid
		if repoName == "" {
			log.Fatalf("Cannot link repository: No valid repository name provided")
		}
		
		fmt.Printf("Linking repository: %s\n", repoName)
		
		// We need to get the remote URI for the repository
		var remoteURI string
		if repoToLink.RemoteUri != "" {
			// Use the remote URI from the repository object if available
			remoteURI = repoToLink.RemoteUri
			fmt.Printf("Using remote URI from repository data: %s\n", remoteURI)
		} else {
			// Otherwise construct a likely URI based on the connection and repository name
			// This is a best guess and may need adjustment for different Git providers
			for _, conn := range cfg.Connections {
				if conn.Name == connNameToLink && conn.GitlabConfig != nil {
					// If we have GitLab config, use the host URI to construct a repo URL
					remoteURI = fmt.Sprintf("%s/%s.git", strings.TrimSuffix(conn.GitlabConfig.HostURI, "/"), repoName)
					break
				}
			}
			
			// If we still don't have a remote URI, prompt the user
			if remoteURI == "" {
				fmt.Println("Please enter the remote URI for the repository (e.g., https://gitlab.com/namespace/repo.git):")
				fmt.Print("Remote URI: ")
				fmt.Scanln(&remoteURI)
				
				if remoteURI == "" {
					log.Fatalf("Remote URI is required to link a repository")
				}
			} else {
				fmt.Printf("Constructed remote URI: %s\n", remoteURI)
			}
		}
		
		// Link the repository with the remote URI
		err = client.LinkRepository(projectIDValue, locationValue, connNameToLink, repoName, remoteURI)
		if err != nil {
			log.Fatalf("Failed to link repository: %v", err)
		}
		
		fmt.Printf("Successfully linked repository %s to connection %s\n", repoName, connNameToLink)

	case "delete-connection":
		// If connection name not provided via command line, get it from config
		connNameToDelete := *connectionName
		if connNameToDelete == "" {
			// If there's only one connection in config, use that
			if len(cfg.Connections) == 1 {
				connNameToDelete = cfg.Connections[0].Name
				fmt.Printf("Using connection name from config: %s\n", connNameToDelete)
			} else if len(cfg.Connections) > 1 {
				// If multiple connections, show the available options
				fmt.Println("Multiple connections found in config. Please specify which one to delete with --name flag:")
				for i, conn := range cfg.Connections {
					fmt.Printf("%d. %s\n", i+1, conn.Name)
				}
				os.Exit(1)
			} else {
				log.Fatalf("No connections found in config file")
			}
		}
		
		log.Printf("Deleting connection %s in project %s (location: %s)", 
			connNameToDelete, projectIDValue, locationValue)
		
		err := client.DeleteConnection(projectIDValue, locationValue, connNameToDelete)
		if err != nil {
			log.Fatalf("Failed to delete connection: %v", err)
		}
		fmt.Printf("Successfully deleted connection: %s\n", connNameToDelete)
	
	case "inspect":
		// This action provides detailed inspection of a connection's configuration and status
		// If connection name not provided via command line, get it from config
		connNameToInspect := *connectionName
		if connNameToInspect == "" {
			// If there's only one connection in config, use that
			if len(cfg.Connections) == 1 {
				connNameToInspect = cfg.Connections[0].Name
				fmt.Printf("Using connection name from config: %s\n", connNameToInspect)
			} else if len(cfg.Connections) > 1 {
				// If multiple connections, show the available options
				fmt.Println("Multiple connections found in config. Please specify which one to inspect with --name flag:")
				for i, conn := range cfg.Connections {
					fmt.Printf("%d. %s\n", i+1, conn.Name)
				}
				os.Exit(1)
			} else {
				log.Fatalf("No connections found in config file")
			}
		}
		
		log.Printf("Inspecting connection %s in project %s (location: %s)", 
			connNameToInspect, projectIDValue, locationValue)
		
		fmt.Printf("Inspecting connection %s in project %s (location: %s)...\n\n", 
			connNameToInspect, projectIDValue, locationValue)
		err := client.InspectConnection(projectIDValue, locationValue, connNameToInspect)
		if err != nil {
			log.Fatalf("Failed to inspect connection: %v", err)
		}
		
	default:
		log.Fatalf("Unknown action: %s. Supported actions are: list-connections, create-gitlab, delete-connection, list-linkable-repos, inspect", *action)
	}
}
