package destination

import (
	"bytes"
	"path/filepath"
	"testing"

	"git.quad4.io/Networks/Reticulum-Go/pkg/common"
	"git.quad4.io/Networks/Reticulum-Go/pkg/identity"
)

type mockTransport struct {
	config     *common.ReticulumConfig
	interfaces map[string]common.NetworkInterface
}

func (m *mockTransport) GetConfig() *common.ReticulumConfig {
	return m.config
}

func (m *mockTransport) GetInterfaces() map[string]common.NetworkInterface {
	return m.interfaces
}

func (m *mockTransport) RegisterDestination(hash []byte, dest interface{}) {
}

type mockInterface struct {
	common.BaseInterface
}

func (m *mockInterface) Send(data []byte, address string) error {
	return nil
}

func TestNewDestination(t *testing.T) {
	id, _ := identity.New()
	transport := &mockTransport{config: &common.ReticulumConfig{}}

	dest, err := New(id, IN|OUT, SINGLE, "testapp", transport, "testaspect")
	if err != nil {
		t.Fatalf("New failed: %v", err)
	}
	if dest == nil {
		t.Fatal("New returned nil")
	}

	if dest.ExpandName() != "testapp.testaspect" {
		t.Errorf("Expected name testapp.testaspect, got %s", dest.ExpandName())
	}

	hash := dest.GetHash()
	if len(hash) != 16 {
		t.Errorf("Expected hash length 16, got %d", len(hash))
	}
}

func TestFromHash(t *testing.T) {
	id, _ := identity.New()
	transport := &mockTransport{}
	hash := make([]byte, 16)

	dest, err := FromHash(hash, id, SINGLE, transport)
	if err != nil {
		t.Fatalf("FromHash failed: %v", err)
	}
	if !bytes.Equal(dest.GetHash(), hash) {
		t.Error("Hashes don't match")
	}
}

func TestRequestHandlers(t *testing.T) {
	id, _ := identity.New()
	dest, _ := New(id, IN, SINGLE, "test", &mockTransport{})

	path := "test/path"
	response := []byte("hello")

	err := dest.RegisterRequestHandler(path, func(p string, d []byte, rid []byte, lid []byte, ri *identity.Identity, ra int64) []byte {
		return response
	}, ALLOW_ALL, nil)
	if err != nil {
		t.Fatalf("RegisterRequestHandler failed: %v", err)
	}

	result := dest.HandleRequest(path, nil, nil, nil, nil, 0)
	if !bytes.Equal(result, response) {
		t.Errorf("Expected response %q, got %q", response, result)
	}

	if !dest.DeregisterRequestHandler(path) {
		t.Error("DeregisterRequestHandler failed")
	}
}

func TestEncryptDecrypt(t *testing.T) {
	id, _ := identity.New()
	dest, _ := New(id, IN|OUT, SINGLE, "test", &mockTransport{})

	plaintext := []byte("hello world")
	ciphertext, err := dest.Encrypt(plaintext)
	if err != nil {
		t.Fatalf("Encrypt failed: %v", err)
	}

	decrypted, err := dest.Decrypt(ciphertext)
	if err != nil {
		t.Fatalf("Decrypt failed: %v", err)
	}

	if !bytes.Equal(plaintext, decrypted) {
		t.Errorf("Decrypted data doesn't match: %q vs %q", decrypted, plaintext)
	}
}

func TestRatchets(t *testing.T) {
	tmpDir := t.TempDir()
	ratchetPath := filepath.Join(tmpDir, "ratchets")

	id, _ := identity.New()
	dest, _ := New(id, IN|OUT, SINGLE, "test", &mockTransport{})

	if !dest.EnableRatchets(ratchetPath) {
		t.Fatal("EnableRatchets failed")
	}

	err := dest.RotateRatchets()
	if err != nil {
		t.Fatalf("RotateRatchets failed: %v", err)
	}

	ratchets := dest.GetRatchets()
	if len(ratchets) != 1 {
		t.Errorf("Expected 1 ratchet, got %d", len(ratchets))
	}
}

func TestPlainDestination(t *testing.T) {
	id, _ := identity.New()
	dest, _ := New(id, IN|OUT, PLAIN, "test", &mockTransport{})

	plaintext := []byte("plain text")
	ciphertext, _ := dest.Encrypt(plaintext)
	if !bytes.Equal(plaintext, ciphertext) {
		t.Error("Plain destination should not encrypt")
	}

	decrypted, _ := dest.Decrypt(ciphertext)
	if !bytes.Equal(plaintext, decrypted) {
		t.Error("Plain destination should not decrypt")
	}
}
