use std::{process, sync::Arc};

use auth_trait::Auth;
use graceful_restart::LOCK;
use log::{error, info};
use ssl_trait::CertByHost;
use tokio::{net::TcpListener, select};
use tokio_util::sync::CancellationToken;

// use kill_port::kill_port;
use crate::{Mailer, Result, accept};

pub async fn bind<A: Auth, M: Mailer>(
  listener: TcpListener,
  auth: A,
  mailer: Arc<M>,
  ssl: impl CertByHost,
  cancel_token: CancellationToken,
) -> Result<()> {
  {
    let addr = listener.local_addr()?;
    let pid = process::id();
    info!("[{pid}] SMTP {addr} with implicit TLS");

    let _ = sys_notify::ready();

    loop {
      select! {
        _ = cancel_token.cancelled() => {
          break;
        },
        result = listener.accept() => {
          match result {
            Ok((stream, addr)) => {
              tokio::spawn({
                let auth = auth.clone();
                let mailer = mailer.clone();
                let ssl = ssl.clone();
                #[allow(clippy::await_holding_lock)]
                async move {
                  let _guard = LOCK.read();
                  accept(stream, addr, auth, mailer, ssl).await;
                }
              });
            }
            Err(e) => {
              error!("accept error: {e}");
              break;
            }
          }
        }
      }
    }
    drop(listener);
  }

  let _guard = LOCK.write();

  Ok(())
}
