package services

import (
	"context"
	"io"
	"log"

	"ur.gs/crockery/internal/imap"
	"ur.gs/crockery/internal/smtp"
	"ur.gs/crockery/internal/store"
)

type Interface interface {
	Run() // Run the services
}

func New(ctx context.Context, datastore store.Interface) (Interface, error) {
	ctx, cancelFunc := context.WithCancel(ctx)

	return &concrete{
		ctx:    ctx,
		cancel: cancelFunc,
		store:  datastore,
	}, nil
}

type genServer interface {
	Run()

	io.Closer
}

type concrete struct {
	ctx    context.Context
	cancel context.CancelFunc
	store  store.Interface

	// This is only mutated in the Run() method
	servers []genServer
}

// For now, bind unconditionally to these ports, on IPv4 + IPv6:
// 25  - incoming SMTP (STARTTLS)
// 587 - outgoing SMTP (STARTTLS)
// 143 - IMAP (STARTTLS)
// 993 - IMAP (TLS)
//
// Each listener gets incoming connections serviced against the passed-in store.
func (c *concrete) Run() {
	c.servers = []genServer{
		smtp.NewServer(c.cancel, c.store, false),
		smtp.NewServer(c.cancel, c.store, true),

		imap.NewServer(c.cancel, c.store, false),
		imap.NewServer(c.cancel, c.store, true),
	}

	log.Print("Running all services")
	for _, srv := range c.servers {
		go srv.Run()
	}

	<-c.ctx.Done()

	log.Print("Closing all services")
	for _, srv := range c.servers {
		srv.Close()
	}
}
