import logging
import json

from flask import request, make_response, Blueprint

from app import billing as stripe
from data import model
from auth.auth import process_auth
from auth.permissions import ModifyRepositoryPermission
from util.invoice import renderInvoiceToHtml
from util.useremails import send_invoice_email, send_subscription_change, send_payment_failed
from util.names import parse_repository_name
from util.http import abort
from endpoints.trigger import BuildTrigger, ValidationRequestException, SkipRequestException
from endpoints.common import start_build


logger = logging.getLogger(__name__)

webhooks = Blueprint('webhooks', __name__)


@webhooks.route('/stripe', methods=['POST'])
def stripe_webhook():
  request_data = request.get_json()
  logger.debug('Stripe webhook call: %s' % request_data)

  customer_id = request_data.get('data', {}).get('object', {}).get('customer', None)
  user = model.get_user_or_org_by_customer_id(customer_id) if customer_id else None

  event_type = request_data['type'] if 'type' in request_data else None
  if event_type == 'charge.succeeded':
    invoice_id = request_data['data']['object']['invoice']

    if user and user.invoice_email:
      # Lookup the invoice.
      invoice = stripe.Invoice.retrieve(invoice_id)
      if invoice:
        invoice_html = renderInvoiceToHtml(invoice, user)
        send_invoice_email(user.email, invoice_html)

  elif event_type.startswith('customer.subscription.'):
    cust_email = user.email if user is not None else 'unknown@domain.com'
    quay_username = user.username if user is not None else 'unknown'

    change_type = ''
    if event_type.endswith('.deleted'):
      plan_id = request_data['data']['object']['plan']['id']
      change_type = 'canceled %s' % plan_id
      send_subscription_change(change_type, customer_id, cust_email, quay_username)
    elif event_type.endswith('.created'):
      plan_id = request_data['data']['object']['plan']['id']
      change_type = 'subscribed %s' % plan_id
      send_subscription_change(change_type, customer_id, cust_email, quay_username)
    elif event_type.endswith('.updated'):
      if 'previous_attributes' in request_data['data']:
        if 'plan' in request_data['data']['previous_attributes']:
          old_plan = request_data['data']['previous_attributes']['plan']['id']
          new_plan = request_data['data']['object']['plan']['id']
          change_type = 'switched %s -> %s' % (old_plan, new_plan)
          send_subscription_change(change_type, customer_id, cust_email, quay_username)

  elif event_type == 'invoice.payment_failed':
    if user:
      send_payment_failed(user.email, user.username)

  return make_response('Okay')


@webhooks.route('/push/<path:repository>/trigger/<trigger_uuid>', methods=['POST'])
@webhooks.route('/push/trigger/<trigger_uuid>', methods=['POST'], defaults={'repository': ''})
@process_auth
def build_trigger_webhook(trigger_uuid, **kwargs):
  logger.debug('Webhook received with uuid %s', trigger_uuid)

  try:
    trigger = model.get_build_trigger(trigger_uuid)
  except model.InvalidBuildTriggerException:
    # It is ok to return 404 here, since letting an attacker know that a trigger UUID is valid
    # doesn't leak anything
    abort(404)

  namespace = trigger.repository.namespace_user.username
  repository = trigger.repository.name
  permission = ModifyRepositoryPermission(namespace, repository)
  if permission.can():
    handler = BuildTrigger.get_trigger_for_service(trigger.service.name)

    logger.debug('Passing webhook request to handler %s', handler)
    config_dict = json.loads(trigger.config)
    try:
      specs = handler.handle_trigger_request(request, trigger.auth_token,
                                             config_dict)
      dockerfile_id, tags, name, subdir, metadata = specs

    except ValidationRequestException:
      # This was just a validation request, we don't need to build anything
      return make_response('Okay')

    except SkipRequestException:
      # The build was requested to be skipped
      return make_response('Okay')

    pull_robot_name = model.get_pull_robot_name(trigger)
    repo = model.get_repository(namespace, repository)
    start_build(repo, dockerfile_id, tags, name, subdir, False, trigger,
                pull_robot_name=pull_robot_name, trigger_metadata=metadata)

    return make_response('Okay')

  abort(403)