package smb

import (
	"context"
	"fmt"
	"net"
	"time"

	"github.com/praetorian-inc/fingerprintx/pkg/plugins"
	"github.com/praetorian-inc/fingerprintx/pkg/plugins/services/smb"
	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
	zgrabsmb "github.com/zmap/zgrab2/lib/smb/smb"
)

type smbInfoDialFunc func(context.Context) (net.Conn, error)

// ==== private helper functions/methods ====

// collectSMBv2Metadata collects metadata for SMBv2 services.
// @memo
func collectSMBv2Metadata(ctx context.Context, executionId string, host string, port int, timeout time.Duration) (*plugins.ServiceSMB, error) {
	if timeout == 0 {
		timeout = 5 * time.Second
	}
	dialer := protocolstate.GetDialersWithId(executionId)
	if dialer == nil {
		return nil, fmt.Errorf("dialers not initialized for %s", executionId)
	}

	conn, err := dialer.Fastdialer.Dial(ctx, "tcp", net.JoinHostPort(host, fmt.Sprintf("%d", port)))
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()

	metadata, err := smb.DetectSMBv2(conn, timeout)
	if err != nil {
		return nil, err
	}
	return metadata, nil
}

// getSMBInfo
func getSMBInfo(conn net.Conn, setupSession, v1 bool) (*zgrabsmb.SMBLog, error) {
	_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
	defer func() {
		_ = conn.SetDeadline(time.Time{})
	}()

	result, err := zgrabsmb.GetSMBLog(conn, setupSession, v1, false)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func updateSMBv1Support(ctx context.Context, result *zgrabsmb.SMBLog, dial smbInfoDialFunc) {
	if result == nil || result.SupportV1 {
		return
	}

	conn, err := dial(ctx)
	if err != nil {
		return
	}
	defer func() {
		_ = conn.Close()
	}()

	v1Result, err := getSMBInfo(conn, false, true)
	if err != nil || v1Result == nil || !v1Result.SupportV1 {
		return
	}
	result.SupportV1 = true
}
