/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package aipo.batch.userinfo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;

import aipo.batch.utils.ALBatchMailUtils;
import aipo.batch.utils.BatchUtils;
import aipo.webservice.util.WsUtils;

import com.aimluck.eip.account.util.AccountUtils;
import com.aimluck.eip.cayenne.om.portlet.EipMMailAccount;
import com.aimluck.eip.cayenne.om.security.TurbineUser;
import com.aimluck.eip.mail.util.ALMailUtils;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.orm.query.SelectQuery;
import com.aimluck.eip.util.ALEipUtils;

/**
 * <HR>
 * <p>
 * 
 * メールアカウント割当のファイルを読み込み、Aipoのデータベースにインポートします。 <br>
 * <P>
 * <HR>
 * <P>
 */
public class MailAccountImporter {
  /** ロガー */
  private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(UserImporter.class.getName());

  /** プロパティファイル */
  private static final String PROPERTY_FILE = JetspeedResources.getString("aipo.conf", "") + File.separator + "Batch.properties";

  /** メールアカウントファイル名 */
  private static final String MAIL_ACCOUNT_FILE_NAME = (String) BatchUtils.getProperties(PROPERTY_FILE).get("import.mailaccount.file.name");

  /** Webメール設定ファイル */
  private static final String SETTING_FILE_PATH = JetspeedResources.getString("aipo.conf", "") + File.separator + "WebMailAdminSettings.properties";

  /** SMTPサーバ名 */
  private static final String DEFAULT_SMTP_SERVER = (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("smtpserver_name");

  /** POP3サーバ名 */
  private static final String DEFAULT_POP3_SERVER = (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("pop3server_name");

  /** SMTPポート */
  private static final String DEFAULT_SMTP_PORT = (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("smtp_port");

  /** POP3ポート */
  private static final String DEFAULT_POP3_PORT = (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("pop3_port");

  /** ユーザー削除フラグ：False */
  private static final String USER_DISABLED_FALSE = "F";

  /**
   * メールアカウント割当インポート
   * <p>
   * メールアカウント割当のファイルを読み込み、Aipoのデータベースにインポートします。<br>
   * 
   * @param dataContext
   * 
   * @return boolean True(処理成功)、False(処理失敗)
   * @exception DBエラー
   *                、ファイルなし 、データ件数エラーの際に発生
   */
  public boolean importMailAccount(DataContext dataContext) throws Exception {

    int updateCounter = 0;
    int insertCounter = 0;
    int deleteCounter = 0;
    BufferedReader reader = null;

    try {
      // 開始メッセージをログに出力
      logger.info("メールアカウント割当情報更新開始");

      // CSVデータファイル
      File csvFile = new File(BatchUtils.IMPORT_CSV_FILE_PATH + File.separator + MAIL_ACCOUNT_FILE_NAME); // データファイル
      if (!csvFile.exists()) {
        throw new FileNotFoundException();
      }

      List<String[]> mailAccountFileList = new ArrayList<String[]>();

      reader = new BufferedReader(new InputStreamReader(new FileInputStream(csvFile), BatchUtils.FILE_ENCODING));

      // 最終行まで読み込む
      String line = "";
      while ((line = reader.readLine()) != null) {
        String[] s = line.split(BatchUtils.SEPARATOR);
        String loginId = BatchUtils.trimDoubleQuotes(s[0]); // ログインID
        String accountName = BatchUtils.trimDoubleQuotes(s[1]); // アカウント名
        String mailAddress = BatchUtils.trimDoubleQuotes(s[2]); // メールアドレス
        String mailUserName = BatchUtils.trimDoubleQuotes(s[3]); // 表示名

        logger.debug("mail account loginId -> " + loginId + ".");

        try {
          // ログインIDでユーザー情報を検索する
          SelectQuery<TurbineUser> userQuery = Database.query(TurbineUser.class);
          // 検索条件：ログイン名と一致
          Expression userExp = ExpressionFactory.matchExp(TurbineUser.LOGIN_NAME_PROPERTY, loginId);
          userQuery.setQualifier(userExp); // 検索実行
          List<TurbineUser> userList = userQuery.fetchList();

          // <ユーザー情報>件数が0件の場合、ログを出力し、次のレコードの処理を行う。
          if (userList.size() == 0) {
            logger.warn("[更新不可]ユーザーが存在しません。　" + line);
            continue;
          }
          TurbineUser user = userList.get(0);
          logger.debug("mail account user_id -> " + user.getUserId() + ", email -> " + mailAddress + ".");

          // メールアドレスでアカウント情報を検索する
          SelectQuery<EipMMailAccount> query = Database.query(EipMMailAccount.class);
          // 検索条件：メールアドレスと一致
          Expression exp1 = ExpressionFactory.matchExp(EipMMailAccount.MAIL_ADDRESS_PROPERTY, mailAddress);
          // 検索条件：ユーザーIDと一致
          Expression exp2 = ExpressionFactory.matchExp(EipMMailAccount.USER_ID_PROPERTY, user.getUserId());
          query.setQualifier(exp1.andExp(exp2)); // 検索実行
          List<EipMMailAccount> resultList = query.fetchList();

          // アカウント情報件数が2件以上の場合、ログを出力し、処理を継続する。
          if (resultList.size() >= 2) {
            logger.warn("[更新不可]メールアドレスに一致するメールアカウントが2件以上見つかりました。　" + line);
          }

          // メールアドレスからPOP3ユーザー名を切り出す。
          String pop3UserName = mailAddress.substring(0, mailAddress.indexOf("@"));

          // メールアカウント情報件数が1件の場合
          if (resultList.size() == 1) {
            // メールアカウント情報を更新する。
            EipMMailAccount account = resultList.get(0);
            logger.debug("account update -> " + account.getAccountId() + " start.");
            account.setPop3userName(pop3UserName);
            // POP3パスワードはハッシュ化する
            account.setPop3password(ALMailUtils.getEncryptedMailAccountPasswd(WsUtils.getMD5Digest(pop3UserName).getBytes()));
            account.setAccountName(accountName);
            account.setMailUserName(mailUserName);
            account.setSmtpserverName(DEFAULT_SMTP_SERVER);
            account.setPop3serverName(DEFAULT_POP3_SERVER);
            account.setSmtpPort(DEFAULT_SMTP_PORT);
            account.setPop3Port(DEFAULT_POP3_PORT);
            account.setUpdateDate(new Date());
            dataContext.commitChanges();

            // 更新件数をインクリメントする。
            updateCounter++;
          }

          // メールアカウント情報件数が0件の場合
          if (resultList.size() == 0) {
            logger.debug("account insert -> " + accountName + " start.");

            ALBatchMailUtils.insertMailAccountData(
              dataContext,
              user.getUserId(),
              accountName,
              ALBatchMailUtils.ACCOUNT_TYPE_INIT,
              mailAddress,
              mailUserName,
              DEFAULT_SMTP_SERVER,
              Integer.parseInt(DEFAULT_SMTP_PORT),
              DEFAULT_POP3_SERVER,
              Integer.parseInt(DEFAULT_POP3_PORT),
              pop3UserName, // POP3ユーザー名
              WsUtils.getMD5Digest(pop3UserName), // POP3パスワード(insertMailAccountData内でハッシュ化)
              0, // 送信時認証フラグ
              "", // 送信時認証（SMTP認証のユーザーID）
              "", // 送信時認証（SMTP認証のパスワード）
              0, // 受信時認証フラグ
              1, // 受信時メール削除フラグ(リリース時は1)
              0, // 受信時メール削除日数指定フラグ
              0, // 受信時メール削除日数
              "1", // 既未受信フラグ
              "", // 署名
              0, // 送信時の暗号化方式
              0);
            dataContext.commitChanges();

            // 登録件数をインクリメントする。
            insertCounter++;
          }

        } catch (Exception e) {
          // ログを出力し、処理を継続する。
          logger.warn("[更新不可]エラーが発生しました。　" + line + e.toString());
        }

        // メールアドレスを<メールアカウント割当ファイル配列>に追加で格納する。
        mailAccountFileList.add(new String[] { loginId, mailAddress });
      }

      // メールアカウント割当ファイルをクローズする。
      reader.close();
      reader = null;

      // メールアカウントの削除
      logger.debug("mail account delete start.");

      // ①メールアカウント情報の追加レコードのみを抽出する。

      // メールアカウント情報の全リスト(Aipo管理アカウントを除く)
      SelectQuery<EipMMailAccount> mailAccountQuery = Database.query(EipMMailAccount.class);
      Expression expMailAccountQuery1 = ExpressionFactory.noMatchExp(EipMMailAccount.USER_ID_PROPERTY, 1);
      mailAccountQuery.setQualifier(expMailAccountQuery1);

      mailAccountQuery.orderAscending(EipMMailAccount.USER_ID_PROPERTY);
      List<EipMMailAccount> mailAccountList = mailAccountQuery.fetchList();

      // メールアカウント情報の削除用リスト
      List<EipMMailAccount> delList = new ArrayList<EipMMailAccount>();

      for (EipMMailAccount mailAccount : mailAccountList) {
        // ユーザー情報のEメールと一致するレコードがあるか判定
        SelectQuery<TurbineUser> userQuery = Database.query(TurbineUser.class);
        Expression exp1 = ExpressionFactory.matchExp(TurbineUser.EMAIL_PROPERTY, mailAccount.getMailAddress());
        userQuery.setQualifier(exp1); // メールアドレス一致
        // remove start 運用フェーズ課題・障害台帳No.144
        // Expression exp2 =
        // ExpressionFactory.matchExp(TurbineUser.DISABLED_PROPERTY,
        // USER_DISABLED_FALSE);
        // userQuery.andQualifier(exp2); // 削除フラグ = False(有効)
        // remove end
        // 一致しなければ、追加(割当)のメールアカウント情報
        if (userQuery.getCount() == 0) {
          delList.add(mailAccount);
        }
      }

      // ②削除リストに対してファイルに存在するかどうか判定
      for (Iterator<EipMMailAccount> dbMailAccount = delList.iterator(); dbMailAccount.hasNext();) {
        EipMMailAccount mailAccount = dbMailAccount.next();

        String dbMailAccountLoginId = ALEipUtils.getBaseUser(mailAccount.getUserId()).getUserName();

        for (String[] fileMailAccount : mailAccountFileList) {
          // メールアドレスとログインIDが一致したら削除リストから削除
          if (fileMailAccount[0].equals(dbMailAccountLoginId) && fileMailAccount[1].equals(mailAccount.getMailAddress())) {
            dbMailAccount.remove();
            continue;
          }
        }
      }
      // ③削除リストのメールアカウント情報を削除
      for (EipMMailAccount delMailAccount : delList) {
        logger.debug("mail account deleted account_id -> "
          + delMailAccount.getAccountId()
          + ", user_id -> "
          + delMailAccount.getUserId()
          + ", email -> "
          + delMailAccount.getMailAddress()
          + ".");
        AccountUtils.deleteAccount(delMailAccount.getAccountId(), null);
        deleteCounter++;
      }
      dataContext.commitChanges();

      // 終了メッセージ
      logger.info("メールアカウント割当更新完了　登録件数：[" + insertCounter + "]　更新件数：[" + updateCounter + "]　削除件数：[" + deleteCounter + "]");

    } catch (FileNotFoundException e) {
      logger.warn("メールアカウント割当ファイルが存在しません。");
    } catch (Exception e) {
      try {
        if (reader != null) {
          // メールアカウント割当ファイルをクローズする。
          reader.close();
        }
      } catch (Exception ex) {

      }
      logger.error("メールアカウント割当の更新に失敗しました。", e);
      return false;
    }
    return true;
  }
}
