package helpers

// |@@| C

import (
	"gardening/src/app/container"
	"gardening/src/lib/spy"
	"gardening/src/lib/sql"
	"gardening/src/lib/time"
	"gardening/sup/canister"
	"github.com/spf13/afero"
	"gitlab.com/ccyrillee/kitcla"
)

const OperatingSystemMemory = "memory"
const OperatingSystemNative = "native"
const DbIplFs = "fs"
const DbIplMemory = "memory"
const DbIplMock = "mock"
const DbIplPgServer = "pg-server"
const TimeIplReal = "real"
const TimeIplDeterministic = "deterministic"

type ContainerMod struct {
	OperatingSystem string
	Db              string
	SecondaryDb     string
	Time            string
	Spying          bool
}

func configureBuiltServices(c *container.Container, ca *canister.Canister) {
	c.Kit.Kit = *kitcla.New()
}

func configureInterfacesIsolation(c *container.Container, ca *canister.Canister) {
	mod := &ContainerMod{
		OperatingSystem: OperatingSystemMemory,
		Db:              DbIplMemory,
		SecondaryDb:     DbIplMock,
		Time:            TimeIplDeterministic,
		Spying:          true,
	}
	configureInterfaces(c, ca, mod)
}

func configureInterfacesReal(c *container.Container, ca *canister.Canister) {
	mod := &ContainerMod{
		OperatingSystem: OperatingSystemNative,
		Db:              DbIplFs,
		SecondaryDb:     DbIplPgServer,
		Time:            TimeIplReal,
		Spying:          false,
	}
	configureInterfaces(c, ca, mod)
}

func configureInterfaces(c *container.Container, ca *canister.Canister, mod *ContainerMod) {
	setSpying(c, mod)
	setOperatingSystemImplementation(c, ca, mod)
	setDbImplementation(c, ca, mod)
	setSecondaryDbImplementation(c, ca, mod)
	setTimeImplementation(c, ca, mod)
}

func setSpying(container *container.Container, mod *ContainerMod) {
	if mod.Spying {
		container.Spy = &spy.Spy{}
	}
}

func setTimeImplementation(container *container.Container, ca *canister.Canister, mod *ContainerMod) {
	if mod.Time == TimeIplReal || mod.Time == "" {
		container.Time = container.TimeIpl
		return
	}
	if mod.Time == TimeIplDeterministic && mod.Spying {
		container.Time = &time.DeterministicTimeIpl{}
		return
	}
	panic("Invalid db")
}

func setDbImplementation(container *container.Container, ca *canister.Canister, mod *ContainerMod) {
	if mod.Db == DbIplFs || mod.Db == "" {
		container.Sql = container.SqliteFsSqlIpl
		return
	}
	if mod.Db == DbIplMemory {
		container.Sql = &sql.SqliteMemorySqlIpl{}
		return
	}
	if mod.Db == DbIplMemory && mod.Spying {
		container.Sql = &sql.SqliteMemorySqlIpl{}
		return
	}
	panic("Invalid db")
}

func setSecondaryDbImplementation(container *container.Container, ca *canister.Canister, mod *ContainerMod) {
	if mod.SecondaryDb == DbIplPgServer || mod.SecondaryDb == "" {
		container.SecondarySql = container.PgSqlIpl
		return
	}
	if mod.SecondaryDb == DbIplMock {
		container.SecondarySql = container.MockSqlIpl
		return
	}
	panic("Invalid secondary db")
}

func setOperatingSystemImplementation(container *container.Container, ca *canister.Canister, mod *ContainerMod) {
	if mod.OperatingSystem == OperatingSystemNative || mod.OperatingSystem == "" {
		container.OperatingSystemInterface = container.OperatingSystemIpl
		return
	}
	if mod.OperatingSystem == OperatingSystemMemory && mod.Spying {
		container.OperatingSystemInterface = ca.MemoryOperatingSystemSpyIpl
		ca.MemoryOperatingSystemSpyIpl.Spy = container.Spy
		ca.MemoryOperatingSystemSpyIpl.MemoryOperatingSystemIpl = ca.MemoryOperatingSystemIpl
		ca.MemoryOperatingSystemIpl.Data.Fs = afero.NewMemMapFs()
		return
	}
	panic("Invalid operating system")
}
