# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import time
import traceback

from app_yaml_helper import AppYamlHelper
from appengine_wrappers import IsDeadlineExceededError, logservice, taskqueue
from branch_utility import BranchUtility
from compiled_file_system import CompiledFileSystem
from custom_logger import CustomLogger
from data_source_registry import CreateDataSources
from environment import GetAppVersion
from gcs_file_system_provider import CloudStorageFileSystemProvider
from github_file_system_provider import GithubFileSystemProvider
from host_file_system_provider import HostFileSystemProvider
from object_store_creator import ObjectStoreCreator
from refresh_tracker import RefreshTracker
from render_refresher import RenderRefresher
from server_instance import ServerInstance
from servlet import Servlet, Request, Response
from timer import Timer


_log = CustomLogger('cron')


class CronServlet(Servlet):
  '''Servlet which runs a cron job.
  '''
  def __init__(self, request, delegate_for_test=None):
    Servlet.__init__(self, request)
    self._delegate = delegate_for_test or CronServlet.Delegate()

  class Delegate(object):
    '''CronServlet's runtime dependencies. Override for testing.
    '''
    def CreateBranchUtility(self, object_store_creator):
      return BranchUtility.Create(object_store_creator)

    def CreateHostFileSystemProvider(self,
                                     object_store_creator,
                                     pinned_commit=None):
      return HostFileSystemProvider(object_store_creator,
                                    pinned_commit=pinned_commit)

    def CreateGithubFileSystemProvider(self, object_store_creator):
      return GithubFileSystemProvider(object_store_creator)

    def CreateGCSFileSystemProvider(self, object_store_creator):
      return CloudStorageFileSystemProvider(object_store_creator)

    def GetAppVersion(self):
      return GetAppVersion()

  def Get(self):
    # Refreshes may time out, and if they do we need to make sure to flush the
    # logs before the process gets killed (Python gives us a couple of
    # seconds).
    #
    # So, manually flush logs at the end of the cron run. However, sometimes
    # even that isn't enough, which is why in this file we use _log and
    # make it flush the log every time its used.
    logservice.AUTOFLUSH_ENABLED = False
    try:
      return self._GetImpl()
    except BaseException:
      _log.error('Caught top-level exception! %s', traceback.format_exc())
    finally:
      logservice.flush()

  def _GetImpl(self):
    # Cron strategy:
    #
    # Collect all DataSources, the PlatformBundle, the ContentProviders, and
    # any other statically renderered contents (e.g. examples content),
    # and spin up taskqueue tasks which will refresh any cached data relevant
    # to these assets.
    #
    # TODO(rockot/kalman): At the moment examples are not actually refreshed
    # because they're too slow.

    _log.info('starting')

    server_instance = self._GetSafeServerInstance()
    master_fs = server_instance.host_file_system_provider.GetMaster()
    if 'commit' in self._request.arguments:
      master_commit = self._request.arguments['commit']
    else:
      master_commit = master_fs.GetCommitID().Get()

    # This is the guy that would be responsible for refreshing the cache of
    # examples. Here for posterity, hopefully it will be added to the targets
    # below someday.
    render_refresher = RenderRefresher(server_instance, self._request)

    # Used to register a new refresh cycle keyed on |master_commit|.
    refresh_tracker = RefreshTracker(server_instance.object_store_creator)

    # Get the default taskqueue
    queue = taskqueue.Queue()

    # GAE documentation specifies that it's bad to add tasks to a queue
    # within one second of purging. We wait 2 seconds, because we like
    # to go the extra mile.
    queue.purge()
    time.sleep(2)

    success = True
    try:
      data_sources = CreateDataSources(server_instance)
      targets = (data_sources.items() +
                 [('content_providers', server_instance.content_providers),