package games.strategy.engine.pbem;

import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import games.strategy.debug.ClientLogger;
import games.strategy.engine.framework.system.HttpProxy;
import games.strategy.net.OpenFileUtility;
import games.strategy.triplea.help.HelpSupport;

/**
 * A poster for www.tripleawarclub.org forum
 * We log in and out every time we post, so we don't need to keep state.
 */
public class TripleAWarClubForumPoster extends AbstractForumPoster {
  private static final long serialVersionUID = -4017550807078258152L;
  private static final String WAR_CLUB_FORUM_URL = "http://www.tripleawarclub.org/modules/newbb";
  private static final Pattern XOOPS_TOKEN_REQUEST_PATTERN =
      Pattern.compile(".*XOOPS_TOKEN_REQUEST[^>]*value=\"([^\"]*)\".*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);

  /**
   * Logs into the website.
   *
   * @throws Exception
   *         if login fails
   */
  private HttpContext login(final CloseableHttpClient client) throws Exception {
    final HttpPost httpPost = new HttpPost("http://www.tripleawarclub.org/user.php");
    final CookieStore cookieStore = new BasicCookieStore();
    final HttpContext httpContext = new BasicHttpContext();
    httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
    final List<NameValuePair> pairs = new ArrayList<>();
    pairs.add(new BasicNameValuePair("uname", getUsername()));
    pairs.add(new BasicNameValuePair("pass", getPassword()));
    pairs.add(new BasicNameValuePair("submit", "Login"));
    pairs.add(new BasicNameValuePair("rememberme", "On"));
    pairs.add(new BasicNameValuePair("xoops_redirect", "/"));
    pairs.add(new BasicNameValuePair("op", "login"));
    httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8));
    HttpProxy.addProxy(httpPost);
    try (CloseableHttpResponse response = client.execute(httpPost, httpContext)) {
      final int status = response.getStatusLine().getStatusCode();
      if (status != HttpURLConnection.HTTP_OK) {
        throw new Exception("Login failed, server returned status: " + status);
      }
      final String body = EntityUtils.toString(response.getEntity());
      final String lowerBody = body.toLowerCase();
      if (lowerBody.contains("incorrect login!")) {
        throw new Exception("Incorrect login credentials");
      }
      if (!lowerBody.contains("thank you for logging in")) {
        System.out.println("Unknown login error, site response " + body);
        throw new Exception("Unknown login error");
      }
    }
    return httpContext;
  }

  /**
   * Post the turn summary and save game to the forum
   * After login we must load the post page to get the XOOPS_TOKEN_REQUEST (which I think is CSRF nounce)
   * then we can post the reply.
   *
   * @param summary
   *        the forum summary
   * @param subject
   *        the forum subject
   * @return true if the post was successful
   */
  @Override
  public boolean postTurnSummary(final String summary, final String subject) {
    try (CloseableHttpClient client =
        HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build()) {
      final HttpContext httpContext = login(client);
      // load the reply page
      final String forumId = "20";
      final String url = WAR_CLUB_FORUM_URL + "/reply.php?forum=" + forumId + "&topic_id="
          + URLEncoder.encode(m_topicId, StandardCharsets.UTF_8.name());
      final String xoopsTokenRequest;
      HttpGet httpGet = new HttpGet(url);
      HttpProxy.addProxy(httpGet);
      try (CloseableHttpResponse response = client.execute(httpGet, httpContext)) {
        final int status = response.getStatusLine().getStatusCode();
        if (status != HttpURLConnection.HTTP_OK) {
          throw new Exception("Could not load reply page: " + url + ". Site returned " + status);
        }
        final String body = EntityUtils.toString(response.getEntity());
        final Matcher m = XOOPS_TOKEN_REQUEST_PATTERN.matcher(body);
        if (!m.matches()) {
          throw new Exception("Unable to find 'XOOPS_TOKEN_REQUEST' form field on reply page");
        }
        xoopsTokenRequest = m.group(1);
      }

      final MultipartEntityBuilder builder = MultipartEntityBuilder.create()
          .addTextBody("subject", subject)
          .addTextBody("message", summary)
          .addTextBody("forum", forumId)
          .addTextBody("topic_id", m_topicId)
          .addTextBody("XOOPS_TOKEN_REQUEST", xoopsTokenRequest)
          .addTextBody("xoops_upload_file[]", "userfile")
          .addTextBody("contents_submit", "Submit")
          .addTextBody("doxcode", "1")
          .addTextBody("dosmiley", "1")
          .addTextBody("dohtml", "1")
          .addTextBody("dobr", "1")
          .addTextBody("editor", "dhtmltextarea");
      if (m_includeSaveGame && m_saveGameFile != null) {
        builder.addBinaryBody("userfile", m_saveGameFile, ContentType.APPLICATION_OCTET_STREAM, m_saveGameFileName);
      }
      final HttpEntity entity = builder.build();
      final HttpPost httpPost = new HttpPost(WAR_CLUB_FORUM_URL + "/post.php");
      HttpProxy.addProxy(httpPost);
      httpPost.setEntity(entity);

      try (CloseableHttpResponse response = client.execute(httpPost, httpContext)) {
        final int status = response.getStatusLine().getStatusCode();
        if (status != HttpURLConnection.HTTP_OK) {
          throw new Exception("Posting summary failed, the server returned status: " + status);
        }
        final String body = EntityUtils.toString(response.getEntity());
        if (!body.toLowerCase().contains("thanks for your submission!")) {
          throw new Exception("Posting summary failed, the server didn't respond with thank you message");
        }
        m_turnSummaryRef =
            "www.tripleawarclub.org/modules/newbb/viewtopic.php?topic_id=" + m_topicId + "&forum=" + forumId;
      }
      // now logout, this is just to be nice, so we don't care if this fails
      try {
        httpGet = new HttpGet("http://www.tripleawarclub.org/user.php?op=logout");
        HttpProxy.addProxy(httpGet);
        client.execute(httpGet, httpContext);
      } catch (final Exception e) {
        ClientLogger.logQuietly("Failed to log out", e);
      }
    } catch (final Exception e) {
      m_turnSummaryRef = e.getMessage();
      ClientLogger.logQuietly(e);
      return false;
    }
    return true;
  }

  @Override
  public String getTestMessage() {
    return "Testing, this will take a couple of seconds...";
  }

  @Override
  public String getHelpText() {
    return HelpSupport.loadHelp("tripleAWarClubForum.html");
  }

  @Override
  public IForumPoster doClone() {
    final TripleAWarClubForumPoster clone = new TripleAWarClubForumPoster();
    clone.setTopicId(getTopicId());
    clone.setIncludeSaveGame(getIncludeSaveGame());
    clone.setAlsoPostAfterCombatMove(getAlsoPostAfterCombatMove());
    clone.setPassword(getPassword());
    clone.setUsername(getUsername());
    clone.setCredentialsSaved(areCredentialsSaved());
    return clone;
  }

  @Override
  public boolean supportsSaveGame() {
    return true;
  }

  @Override
  public String getDisplayName() {
    return "TripleaWarClub.org";
  }

  @Override
  public void viewPosted() {
    final String url = WAR_CLUB_FORUM_URL + "/viewtopic.php?topic_id=" + m_topicId;
    OpenFileUtility.openUrl(url);
  }
}
