package door

import (
	"os"
	"strconv"
	"testing"
	"time"
)

// Create a valid door32.sys file from the config
func CreateDoor32File(config *DropfileConfig, file *os.File) error {
	var err error
	// These are tests, but let's do it right!
	var lines [11]string = [11]string{"0",
		"0", "38400", "", "1", "<User Real Name>",
		"<Handle>", "0", "0", "1", "1"}

	lines[0] = strconv.Itoa(config.Comm_type)
	lines[1] = strconv.Itoa(config.Comm_handle)
	lines[3] = config.BBSID
	lines[4] = strconv.Itoa(config.User_number)
	lines[5] = config.Real_name
	lines[6] = config.Handle
	lines[7] = strconv.Itoa(config.Security_level)
	lines[8] = strconv.Itoa(config.Time_left)
	lines[10] = strconv.Itoa(config.Node)

	// Write out the lines
	for _, line := range lines {
		_, err = file.WriteString(line + "\n")
		if err != nil {
			return err
		}
	}
	return err
}

// Create a valid door.sys file from the config
func CreateDoorSysFile(config *DropfileConfig, file *os.File) error {
	var err error
	// These are tests, but let's do this right.
	var lines [52]string = [52]string{"0", "38400", "8", "0", "57600",
		"Y", "Y", "Y", "Y", "<User Real Name>", "<User From>",
		"XXX XXX-XXXX", "XXX XXX-XXXX", "PASSWORD", "0", "0",
		"01/01/23", "0", "0", "GR", "23", "Y", "123ABC", "0",
		"12/31/99", "0", "Z", "0", "0", "0", "0", "04/01/23",
		"C:\\MAIN", "C:\\GEN", "<SysOp Name>", "<Handle>", "00:00",
		"Y", "Y", "Y", "0", "1", "01/01/23", "00:00", "23:59",
		"0", "0", "0", "0", "<Comment>", "0", "0"}

	if config.Comm_type == 0 {
		lines[0] = "COM0:"
	} else {
		lines[0] = "COM1:"
	}

	lines[3] = strconv.Itoa(config.Node)
	lines[9] = config.Real_name
	lines[14] = strconv.Itoa(config.Security_level)
	lines[17] = strconv.Itoa(config.Time_left * 60)
	lines[18] = strconv.Itoa(config.Time_left)
	lines[25] = strconv.Itoa(config.User_number)
	lines[35] = config.Handle

	// Write out the lines
	for _, line := range lines {
		_, err = file.WriteString(line + "\n")
		if err != nil {
			return err
		}
	}

	return err
}

func TestInvalidDropfileFail(t *testing.T) {
	d := Door{}

	defer func() {
		if r := recover(); r == nil {
			t.Error("ReadDropfile did not panic on missing dropfile.")
		}
	}()

	d.ReadDropfile("dropfile_unknown.sys")
}

func TestMissingDropfileFail(t *testing.T) {
	d := Door{}

	defer func() {
		if r := recover(); r == nil {
			t.Error("ReadDropfile did not panic on missing dropfile.")
		}
	}()

	d.ReadDropfile("MissingDropFile_door.sys")
}

func TestReadDoor32DropFile(t *testing.T) {
	tmpFile, err := os.CreateTemp("", "test-door32.sys-*")
	if err != nil {
		panic("Cannot create temporary file")
	}

	// Clean up the dropfile afterwards
	defer os.Remove(tmpFile.Name())

	dfc := DropfileConfig{2, 20, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 12}

	err = CreateDoor32File(&dfc, tmpFile)
	if err != nil {
		t.Error("tmpFile.WriteString:", err)
	}
	err = tmpFile.Close()
	if err != nil {
		t.Error("tmpFile.Close:", err)
	}

	/*
		// Are my testing dropfiles correct?
		tmpFile, err = os.Create("door32.sys")
		CreateDoor32File(&dfc, tmpFile)
		tmpFile.Close()
	*/

	d := Door{}
	d.ReadDropfile(tmpFile.Name())

	if dfc.Comm_type != d.Config.Comm_type {
		t.Errorf("Comm Type expected %#v, got %#v", dfc.Comm_type, d.Config.Comm_type)
	}
	if dfc.Comm_handle != d.Config.Comm_handle {
		t.Errorf("Comm Handle expected %#v, got %#v", dfc.Comm_handle, d.Config.Comm_handle)
	}
	if dfc.BBSID != d.Config.BBSID {
		t.Errorf("BBSID expected %#v, got %#v", dfc.BBSID, d.Config.BBSID)
	}
	if dfc.User_number != d.Config.User_number {
		t.Errorf("User Number expected %#v, got %#v", dfc.User_number, d.Config.User_number)
	}
	if dfc.Real_name != d.Config.Real_name {
		t.Errorf("Real Name expected %#v, got %#v", dfc.Real_name, d.Config.Real_name)
	}
	if dfc.Handle != d.Config.Handle {
		t.Errorf("Handle expected %#v, got %#v", dfc.Handle, d.Config.Handle)
	}
	if dfc.Time_left != d.Config.Time_left {
		t.Errorf("Time Left expected %#v, got %#v", dfc.Time_left, d.Config.Time_left)
	}
	if dfc.Node != d.Config.Node {
		t.Errorf("Node expected %#v, got %#v", dfc.Node, d.Config.Node)
	}
	start := time.Now()
	timeout := time.Now().Add(time.Duration(dfc.Time_left) * time.Minute)

	// Verify the start time and timeout values have been set correctly.
	startDelta := start.Sub(d.StartTime)
	timeoutDelta := timeout.Sub(d.TimeOut)

	left := d.TimeLeft()
	used := d.TimeUsed()

	if used.Seconds() > 1 {
		t.Errorf("Time Used (from door) > 1 second: %#v", used)
	}

	time_left_seconds := dfc.Time_left * 60

	if time_left_seconds-int(left.Seconds()) > 1 {
		t.Errorf("Time Left differences > 1 second: test %#v door %#v", time_left_seconds, left)
	}
	if startDelta.Seconds() > 1 {
		t.Errorf("Start Time differences: test %#v door %#v delta %#v", start, d.StartTime, startDelta)
	}
	if timeoutDelta.Seconds() > 1 {
		t.Errorf("TimeOut differences: test %#v door %#v delta %#v", timeout, d.TimeOut, timeoutDelta)
	}
}

func TestReadDoorSysDropFile(t *testing.T) {
	tmpFile, err := os.CreateTemp("", "test-door.sys-*")
	if err != nil {
		panic("Cannot create temporary file")
	}

	// Clean up the dropfile afterwards
	defer os.Remove(tmpFile.Name())

	dfc := DropfileConfig{2, 20, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 12}

	err = CreateDoorSysFile(&dfc, tmpFile)
	if err != nil {
		t.Error("tmpFile.WriteString:", err)
	}
	err = tmpFile.Close()
	if err != nil {
		t.Error("tmpFile.Close:", err)
	}

	/*
		// Are my testing dropfiles correct?
		tmpFile, err = os.Create("door.sys")
		CreateDoorSysFile(&dfc, tmpFile)
		tmpFile.Close()
	*/

	d := Door{}
	d.ReadDropfile(tmpFile.Name())

	/*
		if dfc.Comm_type != d.Config.Comm_type {
			t.Errorf("Comm Type expected %#v, got %#v", dfc.Comm_type, d.Config.Comm_type)
		}
		if dfc.Comm_handle != d.Config.Comm_handle {
			t.Errorf("Comm Handle expected %#v, got %#v", dfc.Comm_handle, d.Config.Comm_handle)
		}
		if dfc.BBSID != d.Config.BBSID {
			t.Errorf("BBSID expected %#v, got %#v", dfc.BBSID, d.Config.BBSID)
		}
	*/
	if dfc.User_number != d.Config.User_number {
		t.Errorf("User Number expected %#v, got %#v", dfc.User_number, d.Config.User_number)
	}
	if dfc.Real_name != d.Config.Real_name {
		t.Errorf("Real Name expected %#v, got %#v", dfc.Real_name, d.Config.Real_name)
	}
	if dfc.Handle != d.Config.Handle {
		t.Errorf("Handle expected %#v, got %#v", dfc.Handle, d.Config.Handle)
	}
	if dfc.Time_left != d.Config.Time_left {
		t.Errorf("Time Left expected %#v, got %#v", dfc.Time_left, d.Config.Time_left)
	}
	if dfc.Node != d.Config.Node {
		t.Errorf("Node expected %#v, got %#v", dfc.Node, d.Config.Node)
	}
	start := time.Now()
	timeout := time.Now().Add(time.Duration(dfc.Time_left) * time.Minute)

	// Verify the start time and timeout values have been set correctly.
	startDelta := start.Sub(d.StartTime)
	timeoutDelta := timeout.Sub(d.TimeOut)

	left := d.TimeLeft()
	used := d.TimeUsed()

	if used.Seconds() > 1 {
		t.Errorf("Time Used (from door) > 1 second: %#v", used)
	}

	time_left_seconds := dfc.Time_left * 60

	if time_left_seconds-int(left.Seconds()) > 1 {
		t.Errorf("Time Left differences > 1 second: test %#v door %#v", time_left_seconds, left)
	}
	if startDelta.Seconds() > 1 {
		t.Errorf("Start Time differences: test %#v door %#v delta %#v", start, d.StartTime, startDelta)
	}
	if timeoutDelta.Seconds() > 1 {
		t.Errorf("TimeOut differences: test %#v door %#v delta %#v", timeout, d.TimeOut, timeoutDelta)
	}
}