package models

import (
	"reflect"
	"testing"

	"github.com/k0kubun/pp"
)

func TestMergeNewVersion(t *testing.T) {
	var test = struct {
		a        Packages
		b        Packages
		expected Packages
	}{
		Packages{
			"hoge": {
				Name: "hoge",
			},
		},
		Packages{
			"hoge": {
				Name:       "hoge",
				NewVersion: "1.0.0",
				NewRelease: "release1",
			},
		},
		Packages{
			"hoge": {
				Name:       "hoge",
				NewVersion: "1.0.0",
				NewRelease: "release1",
			},
		},
	}

	test.a.MergeNewVersion(test.b)
	if !reflect.DeepEqual(test.a, test.expected) {
		e := pp.Sprintf("%v", test.a)
		a := pp.Sprintf("%v", test.expected)
		t.Errorf("expected %s, actual %s", e, a)
	}
}

func TestMerge(t *testing.T) {
	var test = struct {
		a        Packages
		b        Packages
		expected Packages
	}{
		Packages{
			"hoge": {Name: "hoge"},
			"fuga": {Name: "fuga"},
		},
		Packages{
			"hega": {Name: "hega"},
			"hage": {Name: "hage"},
		},
		Packages{
			"hoge": {Name: "hoge"},
			"fuga": {Name: "fuga"},
			"hega": {Name: "hega"},
			"hage": {Name: "hage"},
		},
	}

	actual := test.a.Merge(test.b)
	if !reflect.DeepEqual(actual, test.expected) {
		e := pp.Sprintf("%v", test.expected)
		a := pp.Sprintf("%v", actual)
		t.Errorf("expected %s, actual %s", e, a)
	}
}

func TestAddBinaryName(t *testing.T) {
	var tests = []struct {
		in       SrcPackage
		name     string
		expected SrcPackage
	}{
		{
			SrcPackage{Name: "hoge"},
			"curl",
			SrcPackage{
				Name:        "hoge",
				BinaryNames: []string{"curl"},
			},
		},
		{
			SrcPackage{
				Name:        "hoge",
				BinaryNames: []string{"curl"},
			},
			"curl",
			SrcPackage{
				Name:        "hoge",
				BinaryNames: []string{"curl"},
			},
		},
		{
			SrcPackage{
				Name:        "hoge",
				BinaryNames: []string{"curl"},
			},
			"openssh",
			SrcPackage{
				Name:        "hoge",
				BinaryNames: []string{"curl", "openssh"},
			},
		},
	}

	for _, tt := range tests {
		tt.in.AddBinaryName(tt.name)
		if !reflect.DeepEqual(tt.in, tt.expected) {
			t.Errorf("expected %#v, actual %#v", tt.in, tt.expected)
		}
	}
}

func TestFindByBinName(t *testing.T) {
	var tests = []struct {
		in       SrcPackages
		name     string
		expected *SrcPackage
		ok       bool
	}{
		{
			in: map[string]SrcPackage{
				"packA": {
					Name:        "srcA",
					BinaryNames: []string{"binA"},
					Version:     "1.0.0",
				},
				"packB": {
					Name:        "srcB",
					BinaryNames: []string{"binB"},
					Version:     "2.0.0",
				},
			},
			name: "binA",
			expected: &SrcPackage{
				Name:        "srcA",
				BinaryNames: []string{"binA"},
				Version:     "1.0.0",
			},
			ok: true,
		},
		{
			in: map[string]SrcPackage{
				"packA": {
					Name:        "srcA",
					BinaryNames: []string{"binA"},
					Version:     "1.0.0",
				},
				"packB": {
					Name:        "srcB",
					BinaryNames: []string{"binB"},
					Version:     "2.0.0",
				},
			},
			name:     "nobin",
			expected: nil,
			ok:       false,
		},
	}

	for i, tt := range tests {
		act, ok := tt.in.FindByBinName(tt.name)
		if ok != tt.ok {
			t.Errorf("[%d] expected %#v, actual %#v", i, tt.in, tt.expected)
		}
		if act != nil && !reflect.DeepEqual(*tt.expected, *act) {
			t.Errorf("[%d] expected %#v, actual %#v", i, tt.in, tt.expected)
		}
	}
}

func TestPackage_FormatVersionFromTo(t *testing.T) {
	type fields struct {
		Name             string
		Version          string
		Release          string
		NewVersion       string
		NewRelease       string
		Arch             string
		Repository       string
		Changelog        Changelog
		AffectedProcs    []AffectedProcess
		NeedRestartProcs []NeedRestartProcess
	}
	type args struct {
		stat PackageFixStatus
	}
	tests := []struct {
		name   string
		fields fields
		args   args
		want   string
	}{
		{
			name: "fixed",
			fields: fields{
				Name:       "packA",
				Version:    "1.0.0",
				Release:    "a",
				NewVersion: "1.0.1",
				NewRelease: "b",
			},
			args: args{
				stat: PackageFixStatus{
					NotFixedYet: false,
					FixedIn:     "1.0.1-b",
				},
			},
			want: "packA-1.0.0-a -> 1.0.1-b (FixedIn: 1.0.1-b)",
		},
		{
			name: "nfy",
			fields: fields{
				Name:    "packA",
				Version: "1.0.0",
				Release: "a",
			},
			args: args{
				stat: PackageFixStatus{
					NotFixedYet: true,
				},
			},
			want: "packA-1.0.0-a -> Not Fixed Yet",
		},
		{
			name: "nfy",
			fields: fields{
				Name:    "packA",
				Version: "1.0.0",
				Release: "a",
			},
			args: args{
				stat: PackageFixStatus{
					NotFixedYet: false,
					FixedIn:     "1.0.1-b",
				},
			},
			want: "packA-1.0.0-a -> Unknown (FixedIn: 1.0.1-b)",
		},
		{
			name: "nfy2",
			fields: fields{
				Name:    "packA",
				Version: "1.0.0",
				Release: "a",
			},
			args: args{
				stat: PackageFixStatus{
					NotFixedYet: true,
					FixedIn:     "1.0.1-b",
					FixState:    "open",
				},
			},
			want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
		},
		{
			name: "nfy3",
			fields: fields{
				Name:    "packA",
				Version: "1.0.0",
				Release: "a",
			},
			args: args{
				stat: PackageFixStatus{
					NotFixedYet: true,
					FixedIn:     "1.0.1-b",
					FixState:    "open",
				},
			},
			want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := Package{
				Name:             tt.fields.Name,
				Version:          tt.fields.Version,
				Release:          tt.fields.Release,
				NewVersion:       tt.fields.NewVersion,
				NewRelease:       tt.fields.NewRelease,
				Arch:             tt.fields.Arch,
				Repository:       tt.fields.Repository,
				Changelog:        &tt.fields.Changelog,
				AffectedProcs:    tt.fields.AffectedProcs,
				NeedRestartProcs: tt.fields.NeedRestartProcs,
			}
			if got := p.FormatVersionFromTo(tt.args.stat); got != tt.want {
				t.Errorf("Package.FormatVersionFromTo() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_IsRaspbianPackage(t *testing.T) {
	type args struct {
		name string
		ver  string
	}
	tests := []struct {
		name   string
		in     []args
		expect []bool
	}{
		{
			name: "nameRegExp",
			in: []args{
				{
					name: "libraspberrypi-dev",
					ver:  "1.20200811-1",
				},
				{
					name: "rpi-eeprom",
					ver:  "7.10-1",
				},
				{
					name: "python3-rpi.gpio",
					ver:  "0.7.0-0.1~bpo10+1",
				},
				{
					name: "arping",
					ver:  "2.19-6",
				},
				{
					name: "pi-bluetooth",
					ver:  "0.1.14",
				},
			},
			expect: []bool{true, true, true, false, true, false},
		},
		{
			name: "verRegExp",
			in: []args{
				{
					name: "ffmpeg",
					ver:  "7:4.1.6-1~deb10u1+rpt1",
				},
				{
					name: "gcc",
					ver:  "4:8.3.0-1+rpi2",
				},
			},
			expect: []bool{true, true},
		},
		{
			name: "nameList",
			in: []args{
				{
					name: "piclone",
					ver:  "0.16",
				},
			},
			expect: []bool{true},
		},
		{
			name: "debianPackage",
			in: []args{
				{
					name: "apt",
					ver:  "1.8.2.1",
				},
			},
			expect: []bool{false},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			for i, p := range tt.in {
				ret := IsRaspbianPackage(p.name, p.ver)
				if !reflect.DeepEqual(ret, tt.expect[i]) {
					t.Errorf("[%s->%s] expected: %t, actual: %t, in: %#v", tt.name, tt.in[i].name, tt.expect[i], ret, tt.in[i])
				}
			}
		})
	}
}

func Test_parseListenPorts(t *testing.T) {
	tests := []struct {
		name   string
		args   string
		expect PortStat
	}{{
		name: "empty",
		args: "",
		expect: PortStat{
			BindAddress: "",
			Port:        "",
		},
	}, {
		name: "normal",
		args: "127.0.0.1:22",
		expect: PortStat{
			BindAddress: "127.0.0.1",
			Port:        "22",
		},
	}, {
		name: "asterisk",
		args: "*:22",
		expect: PortStat{
			BindAddress: "*",
			Port:        "22",
		},
	}, {
		name: "ipv6_loopback",
		args: "[::1]:22",
		expect: PortStat{
			BindAddress: "[::1]",
			Port:        "22",
		},
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			listenPort, err := NewPortStat(tt.args)
			if err != nil {
				t.Errorf("unexpected error occurred: %s", err)
			} else if !reflect.DeepEqual(*listenPort, tt.expect) {
				t.Errorf("base.parseListenPorts() = %v, want %v", *listenPort, tt.expect)
			}
		})
	}
}
