# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#     * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import datetime
import logging
import webapp2

from google.appengine.ext import ndb
from google.appengine.ext.webapp import template
from google.appengine.ext.db import BadRequestError

# A simple log server for rebaseline-o-matic.
#
# Accepts updates to the same log entry and shows a simple status page.
# Has a special state for the case where there are no NeedsRebaseline
# lines in TestExpectations to avoid cluttering the log with useless
# entries every 30 seconds.
#
# Other than that, new updatelog calls append to the most recent log
# entry until they have the newentry parameter, in which case, it
# starts a new log entry.

LOG_PARAM = "log"
NEW_ENTRY_PARAM = "newentry"
# FIXME: no_needs_rebaseline is never used anymore. Remove support for it.
# Instead, add UI to logs.html to collapse short entries.
NO_NEEDS_REBASELINE_PARAM = "noneedsrebaseline"
NUM_LOGS_PARAM = "numlogs"
BEFORE_PARAM = "before"


class LogEntry(ndb.Model):
    content = ndb.TextProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)
    is_no_needs_rebaseline = ndb.BooleanProperty()


def logs_query():
    return LogEntry.query().order(-LogEntry.date)


class UpdateLog(webapp2.RequestHandler):
    def post(self):
        new_log_data = self.request.POST.get(LOG_PARAM)
        # This entry is set to on whenever a new auto-rebaseline run is going to
        # start logging entries. If this is not on, then the log will get appended
        # to the most recent log entry.
        new_entry = self.request.POST.get(NEW_ENTRY_PARAM) == "on"
        # The case of no NeedsRebaseline lines in TestExpectations is special-cased
        # to always overwrite the previous noneedsrebaseline entry in the log to
        # avoid cluttering the log with useless empty posts. It just updates the
        # date of the entry so that users can see that rebaseline-o-matic is still
        # running.
        # FIXME: no_needs_rebaseline is never used anymore. Remove support for it.
        no_needs_rebaseline = self.request.POST.get(NO_NEEDS_REBASELINE_PARAM) == "on"

        out = "Wrote new log entry."
        if not new_entry or no_needs_rebaseline:
            log_entries = logs_query().fetch(1)
            if log_entries:
                log_entry = log_entries[0]
                log_entry.date = datetime.datetime.now()
                if no_needs_rebaseline:
                    # Don't write out a new log entry for repeated no_needs_rebaseline cases.
                    # The repeated entries just add noise to the logs.
                    if log_entry.is_no_needs_rebaseline:
                        out = "Overwrote existing no needs rebaseline log."
                    else:
                        out = "Wrote new no needs rebaseline log."
                        new_entry = True
                        new_log_data = ""
                elif log_entry.is_no_needs_rebaseline:
                    out = "Previous entry was a no need rebaseline log. Writing a new log."
                    new_entry = True
                else:
                    out = "Added to existing log entry."
                    log_entry.content = log_entry.content + "\n" + new_log_data

        if new_entry or not log_entries:
            log_entry = LogEntry(content=new_log_data, is_no_needs_rebaseline=no_needs_rebaseline)

        try:
            log_entry.put()
        except BadRequestError:
            out = "Created new log entry because the previous one exceeded the max length."
            LogEntry(content=new_log_data, is_no_needs_rebaseline=no_needs_rebaseline).put()

        self.response.out.write(out)


class UploadForm(webapp2.RequestHandler):
    def get(self):
        self.response.out.write(template.render("uploadform.html", {
            "update_log_url": "/updatelog",
            "set_no_needs_rebaseline_url": "/noneedsrebaselines",
            "log_param": LOG_PARAM,
            "new_entry_param": NEW_ENTRY_PARAM,
            "no_needs_rebaseline_param": NO_NEEDS_REBASELINE_PARAM,
        }))


class ShowLatest(webapp2.RequestHandler):
    def get(self):
        query = logs_query()

        before = self.request.get(BEFORE_PARAM)
        if before:
            date = datetime.datetime.strptime(before, "%Y-%m-%dT%H:%M:%SZ")
            query = query.filter(LogEntry.date < date)

        num_logs = self.request.get(NUM_LOGS_PARAM)
        logs = query.fetch(int(num_logs) if num_logs else 3)

        self.response.out.write(template.render("logs.html", {
            "logs": logs,
            "num_logs_param": NUM_LOGS_PARAM,
            "before_param": BEFORE_PARAM,
        }))


routes = [
    ('/uploadform', UploadForm),
    ('/updatelog', UpdateLog),
    ('/', ShowLatest),
]

app = webapp2.WSGIApplication(routes, debug=True)