// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package connparse

import (
	"context"
	"fmt"
	"regexp"
	"strings"

	"github.com/wavetermdev/waveterm/pkg/wshrpc"
	"github.com/wavetermdev/waveterm/pkg/wshutil"
)

const (
	ConnectionTypeWsh  = "wsh"
	ConnectionTypeS3   = "s3"
	ConnectionTypeWave = "wavefile"

	ConnHostCurrent = "current"
	ConnHostWaveSrv = "wavesrv"
)

var windowsDriveRegex = regexp.MustCompile(`^[a-zA-Z]:`)
var wslConnRegex = regexp.MustCompile(`^wsl://[^/]+`)

type Connection struct {
	Scheme string
	Host   string
	Path   string
}

func (c *Connection) GetSchemeParts() []string {
	return strings.Split(c.Scheme, ":")
}

func (c *Connection) GetType() string {
	lastInd := strings.LastIndex(c.Scheme, ":")
	if lastInd == -1 {
		return c.Scheme
	}
	return c.Scheme[lastInd+1:]
}

func (c *Connection) GetPathWithHost() string {
	if c.Host == "" {
		return ""
	}
	if c.Path == "" {
		return c.Host
	}
	if strings.HasPrefix(c.Path, "/") {
		return c.Host + c.Path
	}
	return c.Host + "/" + c.Path
}

func (c *Connection) GetFullURI() string {
	return c.Scheme + "://" + c.GetPathWithHost()
}

func (c *Connection) GetSchemeAndHost() string {
	return c.Scheme + "://" + c.Host
}

func ParseURIAndReplaceCurrentHost(ctx context.Context, uri string) (*Connection, error) {
	conn, err := ParseURI(uri)
	if err != nil {
		return nil, fmt.Errorf("error parsing connection: %v", err)
	}
	if conn.Host == ConnHostCurrent {
		source, err := GetConnNameFromContext(ctx)
		if err != nil {
			return nil, fmt.Errorf("error getting connection name from context: %v", err)
		}

		// RPC context connection is empty for local connections
		if source == "" {
			source = wshrpc.LocalConnName
		}
		conn.Host = source
	}
	return conn, nil
}

func GetConnNameFromContext(ctx context.Context) (string, error) {
	handler := wshutil.GetRpcResponseHandlerFromContext(ctx)
	if handler == nil {
		return "", fmt.Errorf("error getting rpc response handler from context")
	}
	return handler.GetRpcContext().Conn, nil
}

// ParseURI parses a connection URI and returns the connection type, host/path, and parameters.
func ParseURI(uri string) (*Connection, error) {
	split := strings.SplitN(uri, "://", 2)
	var scheme string
	var rest string
	if len(split) > 1 {
		scheme = split[0]
		rest = strings.TrimPrefix(split[1], "//")
	} else {
		rest = split[0]
	}

	var host string
	var remotePath string

	parseGenericPath := func() {
		split = strings.SplitN(rest, "/", 2)
		host = split[0]
		if len(split) > 1 && split[1] != "" {
			remotePath = split[1]
		} else if strings.HasSuffix(rest, "/") {
			// preserve trailing slash
			remotePath = "/"
		} else {
			remotePath = ""
		}
	}
	parseWshPath := func() {
		if strings.HasPrefix(rest, "wsl://") {
			host = wslConnRegex.FindString(rest)
			remotePath = strings.TrimPrefix(rest, host)
		} else {
			parseGenericPath()
		}
	}

	addPrecedingSlash := true

	if scheme == "" {
		scheme = ConnectionTypeWsh
		addPrecedingSlash = false
		if len(rest) != len(uri) {
			// This accounts for when the uri starts with "//", which would get trimmed in the first split.
			parseWshPath()
		} else if strings.HasPrefix(rest, "/~") {
			host = wshrpc.LocalConnName
			remotePath = rest
		} else {
			host = ConnHostCurrent
			remotePath = rest
		}
	} else if scheme == ConnectionTypeWsh {
		parseWshPath()
	} else {
		parseGenericPath()
	}

	if scheme == ConnectionTypeWsh {
		if host == "" {
			host = wshrpc.LocalConnName
		}
		if strings.HasPrefix(remotePath, "/~") {
			remotePath = strings.TrimPrefix(remotePath, "/")
		} else if addPrecedingSlash && (len(remotePath) > 1 && !windowsDriveRegex.MatchString(remotePath) && !strings.HasPrefix(remotePath, "/") && !strings.HasPrefix(remotePath, "~") && !strings.HasPrefix(remotePath, "./") && !strings.HasPrefix(remotePath, "../") && !strings.HasPrefix(remotePath, ".\\") && !strings.HasPrefix(remotePath, "..\\") && remotePath != "..") {
			remotePath = "/" + remotePath
		}
	}

	conn := &Connection{
		Scheme: scheme,
		Host:   host,
		Path:   remotePath,
	}
	return conn, nil
}
