package jdbc

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"strings"

	logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"

	mssql "github.com/microsoft/go-mssqldb"
)

type sqlServerJDBC struct {
	connStr string
}

func (s *sqlServerJDBC) ping(ctx context.Context) pingResult {
	return ping(ctx, "mssql", isSqlServerErrorDeterminate,
		s.connStr)
}

func isSqlServerErrorDeterminate(err error) bool {
	// Error numbers from https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver16
	var sqlErr mssql.Error
	if errors.As(err, &sqlErr) {
		switch sqlErr.Number {
		case 18456:
			// Login failed
			// This is a determinate failure iff we tried to use a real user
			return sqlErr.Message != "login error: Login failed for user ''."
		}
	}
	return false
}

func parseSqlServer(ctx logContext.Context, subname string) (jdbc, error) {
	if !strings.HasPrefix(subname, "//") {
		return nil, errors.New("expected connection to start with //")
	}
	conn := strings.TrimPrefix(subname, "//")

	port := "1433"
	user := "sa"
	var password, host string

	for i, param := range strings.Split(conn, ";") {
		key, value, found := strings.Cut(param, "=")
		if !found && i == 0 {
			//  String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=AdventureWorks;user=<user>;password=<password>";
			if split := strings.Split(param, ":"); len(split) > 1 {
				host = split[0]
				port = split[1]
			} else {
				host = param
			}
			continue
		}

		switch strings.ToLower(key) {
		case "password":
			password = value
		case "spring.datasource.password":
			password = value
		case "server":
			host = value
		case "port":
			port = value
		case "user", "uid", "user id":
			user = value

		}
	}

	if password == "" || host == "" {
		ctx.Logger().WithName("jdbc").
			V(2).
			Info("Skipping invalid SQL Server URL - no password or host found")
		return nil, fmt.Errorf("missing host or password in connection string")
	}

	urlStr := fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=master&connection+timeout=5", user, password, host, port)
	jdbcUrl, err := url.Parse(urlStr)
	if err != nil {
		ctx.Logger().WithName("jdbc").
			V(3).
			Info("Skipping invalid SQL Server URL", "url", urlStr, "err", err)
		return nil, err
	}

	return &sqlServerJDBC{connStr: jdbcUrl.String()}, nil
}
