use std::mem;

use log::info;
use mail_forward::Forward;
use ssl_trait::CertByHost;
use tokio::net::TcpStream;

use crate::{
  Mailer, Result, S, SmtpError, Stream,
  cmd::{handle_cmd, handle_forward_mail, handle_forward_rcpt},
  send_to::SendTo,
  session::{self, Action, read_mail},
  stream::{StreamEnum, perform_tls_handshake},
};

/// SMTP转发会话结构，管理25端口的邮件接收和转发
///
/// 与认证会话不同，转发会话：
/// - 不需要用户认证
/// - 支持STARTTLS升级
/// - 接收邮件后转发给指定的邮箱服务
pub struct Session<F: Forward, M: Mailer, C: CertByHost> {
  /// 流（明文或TLS）
  stream: StreamEnum,
  /// 转发服务
  forward: F,
  /// 邮件发送服务
  mailer: M,
  /// SSL证书提供者
  ssl: C,
  /// 实际的SNI主机名（STARTTLS后设置）
  sni_host: Option<String>,
  /// 邮件发送目标信息
  send_to: SendTo,
}

impl<F: Forward, M: Mailer, C: CertByHost> session::Session for Session<F, M, C> {
  type Stream = StreamEnum;

  fn stream(&mut self) -> &mut Self::Stream {
    &mut self.stream
  }

  async fn handle_cmd(
    &mut self,
    cmd: &str,
    args: &str,
    pending_responses: &mut Vec<String>,
  ) -> Result<Action> {
    match cmd {
      "EHLO" | "HELO" => Ok(Action::Reply(self.handle_ehlo()?)),
      "STARTTLS" => {
        if !self.stream.is_tls() {
          // STARTTLS需要立即处理，不能批量
          self.stream.flush(pending_responses).await?;
          pending_responses.clear();
          self.handle_starttls().await?;
          Ok(Action::Handled)
        } else {
          Ok(S::TLS_ALREADY_ACTIVE.into())
        }
      }
      "MAIL" => Ok(handle_forward_mail(args, &mut self.send_to)),
      "RCPT" => Ok(handle_forward_rcpt(args, &mut self.send_to)),
      _ => Ok(handle_cmd(cmd, &mut self.send_to)),
    }
  }

  async fn handle_data(&mut self) -> Result<()> {
    self.read_and_forward_mail().await
  }
}

impl<F: Forward, M: Mailer, C: CertByHost> Session<F, M, C> {
  /// 创建新的转发会话
  pub fn new(stream: TcpStream, forward: F, mailer: M, ssl: C) -> Self {
    Self {
      stream: StreamEnum::new_plain(stream),
      forward,
      mailer,
      ssl,
      sni_host: None,
      send_to: SendTo::new(),
    }
  }

  /// 向客户端发送SMTP响应（自动添加CRLF）
  async fn send(&mut self, msg: &str) -> Result<()> {
    self.stream.send(msg).await
  }

  /// 运行SMTP转发会话主循环
  pub async fn run(mut self) -> Result<()> {
    session::run_loop(&mut self).await
  }

  /// 获取当前有效的主机名（优先使用SNI主机名）
  fn effective_host(&self) -> Result<&str> {
    if let Some(host) = self.sni_host.as_deref() {
      Ok(host)
    } else if self.stream.is_tls() {
      Err(SmtpError::NoSni)
    } else {
      Ok("localhost")
    }
  }

  /// 处理EHLO/HELO命令
  fn handle_ehlo(&self) -> Result<String> {
    let effective_host = self.effective_host()?;
    if self.stream.is_tls() {
      // TLS已启用，不再提供STARTTLS
      Ok(format!(
        "250-{}\r\n250-PIPELINING\r\n250 8BITMIME",
        effective_host
      ))
    } else {
      // 提供STARTTLS选项
      Ok(format!(
        "250-{}\r\n250-STARTTLS\r\n250-PIPELINING\r\n250 8BITMIME",
        effective_host
      ))
    }
  }

  /// 处理STARTTLS命令
  async fn handle_starttls(&mut self) -> Result<()> {
    // 发送准备升级响应
    self.send(S::STARTTLS_READY).await?;

    // 使用 StreamEnum::None 作为占位符安全地取出流进行 TLS 升级
    if let StreamEnum::Plain(plain_stream) = mem::replace(&mut self.stream, StreamEnum::None) {
      // 重新组合TCP流
      let tcp_stream = plain_stream
        .reader
        .into_inner()
        .unsplit(plain_stream.writer);

      // 使用统一的TLS握手函数（STARTTLS也要求SNI）
      let (tls_stream, sni_host) = perform_tls_handshake(tcp_stream, &self.ssl, true).await?;

      // 保存SNI主机名以供后续使用
      self.sni_host = Some(sni_host);

      // 更新流为TLS模式
      self.stream = StreamEnum::new_tls(tls_stream);
    }

    Ok(())
  }

  /// 读取邮件内容并转发
  async fn read_and_forward_mail(&mut self) -> Result<()> {
    // 读取邮件内容
    let body = read_mail(&mut self.stream).await?;

    if let Some(sender) = self.send_to.take_sender() {
      let to_li = self.send_to.take_to_li();

      // 获取转发目标
      let forward_li = match self.forward_li(&to_li).await {
        Ok(li) => li,
        Err(_) => return Ok(()), // 错误已在函数内处理
      };

      if forward_li.is_empty() {
        return Ok(());
      }

      // 发送邮件
      self.send_mail(&sender, forward_li, body).await?;
    }

    Ok(())
  }

  /// 根据收件人列表获取转发目标
  async fn forward_li(&mut self, to_li: &[String]) -> Result<Vec<String>> {
    let len = to_li.len();
    let li = if len == 0 {
      Vec::new()
    } else if len == 1 {
      // 单个收件人，使用 forward 方法
      match self.forward.forward(&to_li[0]).await {
        Ok(Some(target)) => vec![target],
        Ok(None) => {
          self.send(S::NO_SUCH_USER).await?;
          return Ok(vec![]);
        }
        Err(err) => {
          log::error!("Forward decision failed: {}", err);
          self.send_err("forwarding error").await?;
          return Err(err.into());
        }
      }
    } else {
      // 多个收件人，使用 forward_set 方法进行批量处理和去重
      match self.forward.forward_set(to_li).await {
        Ok(li) => {
          if li.is_empty() {
            self.send(S::NO_SUCH_USER).await?;
            return Ok(vec![]);
          } else {
            li
          }
        }
        Err(err) => {
          log::error!("Forward decision failed: {}", err);
          self.send_err("forwarding error").await?;
          return Err(err.into());
        }
      }
    };
    info!("forward {to_li:?} → {li:?}");
    Ok(li)
  }

  /// 发送邮件到指定目标
  async fn send_mail(&mut self, sender: &str, li: Vec<String>, body: Vec<u8>) -> Result<()> {
    if let Some(mail) = mail_struct::Mail::new(sender, li.clone(), body) {
      match self.mailer.forward(mail).await {
        Ok(_) => {
          info!(
            "Mail from {} forwarded to {} recipients: {:?}",
            sender,
            li.len(),
            li
          );
          self.send(S::MESSAGE_ACCEPTED_FORWARDING).await?;
        }
        Err(err) => {
          log::error!("Mailer send failed for {}: {}", sender, err);
          self.send_err("local error in processing").await?;
        }
      }
    } else {
      log::error!("Failed to construct mail for forwarding from {}", sender);
      self.send_err("mail construction error").await?;
    }
    Ok(())
  }

  /// 发送错误响应
  async fn send_err(&mut self, error_type: &str) -> Result<()> {
    self
      .send(&format!("451 Requested action aborted: {}", error_type))
      .await
  }
}
