package cloudbuild

import (
	"context"
	"fmt"
	"strings"
	"time"

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

// Client is a wrapper around the Cloud Build service
type Client struct {
	service *cloudbuild.Service
}

// NewClient creates a new Cloud Build client using Google's standard authentication
func NewClient(ctx context.Context) (*Client, error) {
	// Create client with appropriate scopes
	opts := []option.ClientOption{
		option.WithScopes("https://www.googleapis.com/auth/cloud-platform"),
	}
	
	// Create service using standard authentication
	service, err := cloudbuild.NewService(ctx, opts...)
	if err != nil {
		return nil, fmt.Errorf("failed to create Cloud Build service: %w", err)
	}
	
	return &Client{service: service}, nil
}

// NewClientWithToken creates a client with a token
// The token is expected to be a valid OAuth2 token
func NewClientWithToken(ctx context.Context, token string) (*Client, error) {
	// Create a token source using oauth2 directly
	tokenSource := oauth2.StaticTokenSource(&oauth2.Token{
		AccessToken: token,
		TokenType:   "Bearer",
	})
	
	// Create client with appropriate scopes and token
	opts := []option.ClientOption{
		option.WithScopes("https://www.googleapis.com/auth/cloud-platform"),
		option.WithTokenSource(tokenSource),
	}
	
	// Create the Cloud Build service
	service, err := cloudbuild.NewService(ctx, opts...)
	if err != nil {
		return nil, fmt.Errorf("failed to create Cloud Build service: %w", err)
	}
	
	return &Client{service: service}, nil
}

// CreateConnection creates a new connection in Cloud Build using v2 API
func (c *Client) CreateConnection(projectID, location, name, connType string) error {
	// Format the parent string as required by the API
	parent := fmt.Sprintf("projects/%s/locations/%s", projectID, location)

	// Create a new connection object
	connection := &cloudbuild.Connection{
		Name:     fmt.Sprintf("%s/connections/%s", parent, name),
		Disabled: false,
	}
	
	// Set connection type based on the input
	switch connType {
	case "GITHUB":
		// GitHub config would be specified here - we don't set it here as we'll add specific config later
	case "GITLAB":
		// GitLab config is handled separately in CreateGitLabConnection
		return fmt.Errorf("for GitLab connections, please use CreateGitLabConnection method")
	default:
		return fmt.Errorf("unsupported connection type: %s", connType)
	}

	// Make the API call to create the connection
	_, err := c.service.Projects.Locations.Connections.Create(
		parent,
		connection,
	).ConnectionId(name).Do()

	if err != nil {
		return fmt.Errorf("error creating connection: %w", err)
	}

	return nil
}

// CreateGitLabConnection creates a new GitLab connection in Cloud Build using v2 API
func (c *Client) CreateGitLabConnection(projectID, location, name string, connConfig *config.GitlabConnectionConfig, annotations map[string]string) error {
	// Format the parent string as required by the API
	parent := fmt.Sprintf("projects/%s/locations/%s", projectID, location)

	// Create the GitLab configuration using the proper v2 API types
	// Set up the credentials first with required token secret versions
	readCredential := &cloudbuild.UserCredential{
		Username:              "oauth2",
		UserTokenSecretVersion: connConfig.ReadTokenVersion,
	}
	
	authCredential := &cloudbuild.UserCredential{
		Username:              "oauth2",
		UserTokenSecretVersion: connConfig.AuthTokenVersion,
	}
	
	// Create the GitLab config
	gitlabConfig := &cloudbuild.GoogleDevtoolsCloudbuildV2GitLabConfig{
		HostUri:                    connConfig.HostURI,
		WebhookSecretSecretVersion: connConfig.WebhookSecretVersion,
		ReadAuthorizerCredential:   readCredential,
		AuthorizerCredential:       authCredential,
	}

	// According to the v2 API, when creating a connection:
	// 1. The Name should not be set - it's determined by the API
	// 2. The connection ID is passed as a parameter to Create()
	connection := &cloudbuild.Connection{
		Disabled:    false,
		Annotations: annotations,
		GitlabConfig: gitlabConfig,
	}

	// Make the API call to create the connection
	_, err := c.service.Projects.Locations.Connections.Create(
		parent,
		connection,
	).ConnectionId(name).Do()

	if err != nil {
		return fmt.Errorf("error creating GitLab connection: %w", err)
	}

	// Successfully created the connection
	// Note: Due to eventual consistency in Google Cloud APIs, the connection
	// might not be immediately available after creation. We'll skip the validation
	// step as it's not necessary to confirm success, and we've already received
	// a successful response from the API.
	
	fmt.Printf("Successfully created GitLab connection: %s\n", 
		fmt.Sprintf("projects/%s/locations/%s/connections/%s", projectID, location, name))

	return nil
}

// GetConnection retrieves an existing connection from Cloud Build using v2 API
func (c *Client) GetConnection(projectID, location, name string) (*cloudbuild.Connection, error) {
	// Format the connection name as required by the API
	connectionName := fmt.Sprintf("projects/%s/locations/%s/connections/%s", projectID, location, name)

	// Make the API call to get the connection
	resp, err := c.service.Projects.Locations.Connections.Get(connectionName).Do()

	if err != nil {
		return nil, fmt.Errorf("error getting connection: %w", err)
	}

	return resp, nil
}

// InspectConnection retrieves detailed information about a connection and its status
func (c *Client) InspectConnection(projectID, location, name string) error {
	// Get the connection details first
	conn, err := c.GetConnection(projectID, location, name)
	if err != nil {
		return fmt.Errorf("error getting connection: %w", err)
	}

	// Print basic connection details
	fmt.Printf("Connection Name: %s\n", conn.Name)
	fmt.Printf("Connection Status: %s\n", statusString(conn.Disabled))
	fmt.Printf("Installation State: %s\n", conn.InstallationState)
	
	// Print GitLab specific details if it's a GitLab connection
	if conn.GitlabConfig != nil {
		fmt.Printf("\nGitLab Connection Details:\n")
		fmt.Printf("Host URI: %s\n", conn.GitlabConfig.HostUri)
		fmt.Printf("Read Auth Username: %s\n", conn.GitlabConfig.ReadAuthorizerCredential.Username)
		// Don't print the actual token versions for security reasons
		fmt.Printf("Read Token Secret: %s\n", shortenSecretName(conn.GitlabConfig.ReadAuthorizerCredential.UserTokenSecretVersion))
		fmt.Printf("Auth Token Secret: %s\n", shortenSecretName(conn.GitlabConfig.AuthorizerCredential.UserTokenSecretVersion))
		
		// Check if webhook is configured
		if conn.GitlabConfig.WebhookSecretSecretVersion != "" {
			fmt.Printf("Webhook Secret: %s\n", shortenSecretName(conn.GitlabConfig.WebhookSecretSecretVersion))
		} else {
			fmt.Println("No webhook configuration found")
		}
		
		// Print annotations if any
		if len(conn.Annotations) > 0 {
			fmt.Println("\nAnnotations:")
			for k, v := range conn.Annotations {
				fmt.Printf("  %s: %s\n", k, v)
			}
		}
	}
	
	return nil
}

// Helper function to get a status string
func statusString(disabled bool) string {
	if disabled {
		return "Disabled"
	}
	return "Enabled"
}

// Helper function to shorten secret version names for display
func shortenSecretName(secretVersion string) string {
	// Just show the last part of the secret name for brevity
	parts := strings.Split(secretVersion, "/")
	if len(parts) > 2 {
		return "..." + parts[len(parts)-2] + "/" + parts[len(parts)-1]
	}
	return secretVersion
}

// ListConnections lists all connections in the specified project and location using v2 API
func (c *Client) ListConnections(projectID, location string) ([]*cloudbuild.Connection, error) {
	// Format the parent string as required by the API
	parent := fmt.Sprintf("projects/%s/locations/%s", projectID, location)

	// Make the API call to list connections
	resp, err := c.service.Projects.Locations.Connections.List(parent).Do()

	if err != nil {
		return nil, fmt.Errorf("error listing connections: %w", err)
	}

	return resp.Connections, nil
}

// DeleteConnection deletes an existing connection from Cloud Build using v2 API
func (c *Client) DeleteConnection(projectID, location, name string) error {
	// Format the connection name as required by the API
	connectionName := fmt.Sprintf("projects/%s/locations/%s/connections/%s", projectID, location, name)

	// Make the API call to delete the connection
	_, err := c.service.Projects.Locations.Connections.Delete(connectionName).Do()

	if err != nil {
		return fmt.Errorf("error deleting connection: %w", err)
	}

	return nil
}

// ListLinkableRepositories lists all repositories that can be linked to the specified connection
// This returns repositories from the source provider (GitHub, GitLab, etc.) that are available
// for linking to Cloud Build
func (c *Client) ListLinkableRepositories(projectID, location, connectionName string) ([]*cloudbuild.Repository, error) {
	// Format the connection name as required by the API
	fullConnectionName := fmt.Sprintf("projects/%s/locations/%s/connections/%s", projectID, location, connectionName)

	// Add debug output to show connection being used
	fmt.Printf("Fetching linkable repositories for connection: %s\n", fullConnectionName)
	
	// Create a list of all repositories from all pages
	var allRepositories []*cloudbuild.Repository
	
	// Use the correct API method: FetchLinkableRepositories instead of List
	call := c.service.Projects.Locations.Connections.FetchLinkableRepositories(fullConnectionName)
	
	// Set a larger page size to ensure we get more results if available
	call = call.PageSize(100)
	
	// Fetch all repositories using the Pages method for proper pagination handling
	fmt.Println("Starting to fetch repositories - this may take a moment...")
	
	// Create a context with timeout to avoid hanging indefinitely
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	
	error := call.Pages(ctx, func(resp *cloudbuild.FetchLinkableRepositoriesResponse) error {
		if resp.Repositories != nil {
			allRepositories = append(allRepositories, resp.Repositories...)
			fmt.Printf("Received %d repositories in this page\n", len(resp.Repositories))
		}
		return nil
	})
	
	if error != nil {
		return nil, fmt.Errorf("error fetching linkable repositories: %w", error)
	}
	
	// Debug output for results
	fmt.Printf("Total linkable repositories found: %d\n", len(allRepositories))
	
	if len(allRepositories) == 0 {
		fmt.Println("No linkable repositories returned from API. This could be due to:")
		fmt.Println("- No repositories exist in the connected GitLab/GitHub account")
		fmt.Println("- Authentication/permission issues with the access token")
		fmt.Println("- Repository visibility settings in GitLab might be restricted")
		fmt.Println("- The repositories haven't been properly indexed by Cloud Build yet")
		fmt.Println("\nTry the following troubleshooting steps:")
		fmt.Println("1. Verify the access token has appropriate permissions in GitLab")
		fmt.Println("2. Check if you can see repositories in the Cloud Build console")
		fmt.Println("3. Try re-creating the connection with a new token")
	}
	
	return allRepositories, nil
}
