/**
 * Copyright (C) 2015-2016 Kaj Magnus Lindberg
 *
 * 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 controllers

import com.debiki.core._
import com.debiki.core.Prelude._
import debiki._
import debiki.EdHttp._
import debiki.dao.CreatePageResult
import talkyard.server._
import talkyard.server.authz.Authz
import javax.inject.Inject
import play.api.libs.json.{JsArray, JsValue, Json}
import play.api.mvc._


/** Saves a {{{<form>}}} as either 1) a new reply, in JSON (for the db) + Yaml (for presentation),
  * or as 2) a new topic — then in title + human friendly body.
  */
class CustomFormController @Inject()(cc: ControllerComponents, edContext: TyContext)
  extends TyController(cc, edContext) {

  import context.security.{throwIndistinguishableNotFound, throwNoUnless}


  def handleJsonReply: Action[JsValue] = PostJsonAction(
        RateLimits.PostReply, maxBytes = MaxPostSize) { request =>
    import request.dao

    val pageId = (request.body \ "pageId").as[PageId]
    val formInputs = (request.body \ "formInputs").as[JsArray]
    val textAndHtml = dao.textAndHtmlMaker.withCompletedFormData(formInputs) getOrIfBad { errorMessage =>
      throwBadRequest("EsE7YK4W0", s"Bad form inputs JSON: $errorMessage")
    }

    val pageMeta = dao.getPageMeta(pageId) getOrElse
      throwIndistinguishableNotFound("EdE2WK0F")

    val categoriesRootLast = dao.getAncestorCategoriesRootLast(pageMeta.categoryId)

    throwNoUnless(Authz.maySubmitCustomForm(
      request.userAndLevels, dao.getGroupIdsOwnFirst(request.user),
      pageMeta, inCategoriesRootLast = categoriesRootLast,
      tooManyPermissions = dao.getPermsOnPages(categoriesRootLast)),
      "EdE2TE4A0")

    request.dao.insertReplySkipAuZ(textAndHtml, pageId, Set.empty, PostType.CompletedForm,
        deleteDraftNr = None, request.whoOrUnknown, request.spamRelatedStuff)
    Ok
  }


  def handleNewTopic: Action[JsValue] = PostJsonAction(
        RateLimits.PostReply, maxBytes = MaxPostSize) { request =>
    import request.dao

    val pageTypeIdString = (request.body \ "pageTypeId").as[String]
    val pageTypeId = pageTypeIdString.toIntOption.getOrThrowBadArgument("EsE6JFU02", "pageTypeId")
    val pageType = PageType.fromInt(pageTypeId).getOrThrowBadArgument("EsE39PK01", "pageTypeId")
    val titleText = (request.body \ "newTopicTitle").as[String]
    val bodyText = (request.body \ "newTopicBody").as[String]
    val titleSourceAndHtml = TitleSourceAndHtml(titleText)
    val bodyTextAndHtml = dao.textAndHtmlMaker.forBodyOrCommentAsPlainTextWithLinks(bodyText)

    // BUG (need not fix now) if there are many sub communities with the same category slug. [4GWRQA28]
    val categorySlug = (request.body \ "categorySlug").as[String]
    val category = request.dao.getCategoryBySlug(categorySlug).getOrThrowBadArgument(
        "EsE0FYK42", s"No category with slug: $categorySlug")

    val res: CreatePageResult = dao.createPageIfAuZ(
          pageType,
          PageStatus.Published,
          inCatId = Some(category.id),
          withTags = Nil,
          anyFolder = None,
          anySlug = None,
          title = titleSourceAndHtml,
          bodyTextAndHtml = bodyTextAndHtml,
          showId = true,
          deleteDraftNr = None,
          reqrAndCreator = request.reqrTargetSelf,
          spamRelReqStuff = request.spamRelatedStuff,
          asAlias = None,
          discussionIds = Set.empty,
          embeddingUrl = None,
          refId = None)

    OkSafeJson(Json.obj("newPageId" -> res.path.pageId))
  }


  def handleExternalForm_unused: Action[Either[MaxSizeExceeded, Map[String, Seq[String]]]] =
        PostFormAction_unused(
            RateLimits.SubmitExternalForm_unused, maxBytes = MaxPostSize,
            allowAnyone = true, skipXsrfCheck = true) { req =>
    import edContext.globals
    import req.dao
    val notifyEmailAdr: St = globals.config.forms.notifyAboutFormsEmailAdr getOrElse {
      throwForbidden("TyE0FORMCONFGD", o"""This site hasn't been configured to save
            custom forms — conf val missing:  talkyard.notifyAboutFormsEmailAddr""")
    }

    val fromIpAdr = req.ip
    val formAsText = req.body.toString
    val formAsHtml = debiki.TextAndHtml.sanitizeAllowLinksAndBlocks(unsafeHtml = formAsText)

    val formNotfEmail = Email.createGenId(
          EmailType.FormFilledIn,
          createdAt = globals.now(),
          sendTo = notifyEmailAdr,
          toUserId = None, // maybe could lookup if happens to match formNotfEmail
          sendFrom = None,
          aboutWhat = None, //  Opt[EmailAbout],
          subject = "Form filled in",
          bodyHtml = formAsHtml,
          smtpMsgId = None,  // hash(salt + ip) + seq nr?
          inReplyToSmtpMsgId = None,
          referencesSmtpMsgIds = Nil)

    dao.saveUnsentEmail(formNotfEmail)
    globals.sendEmail(formNotfEmail, siteId = dao.siteId)
    // I18N, maybe should be a React/Typescript page instead.
    Utils.OkHtml(<div>
        <script>
        function goBackOrClose() {{
          if (history.length >= 2) history.back()
          else window.close();
        }}
        </script>
        <h2>Saved</h2>
        <p>We'll try to remember. Thanks</p>
        <p>
          <button onclick="goBackOrClose()">Okay</button>
        </p>
      </div>)
  }

  def sayCantUseMethodGetHere_unused: Action[U] = GetActionAllowAnyoneRateLimited(
        RateLimits.NoRateLimits) { request =>
    Utils.OkHtml(
          <div>
          <h3>Nothing here to do</h3>
          <p>
          <button onclick="history.back()">Go back</button>
          &nbsp; &nbsp;
          <button onclick="window.close()">Close</button>
          </p>
          </div>)
  }
}
