# -*- coding: utf-8 -*- #
# Copyright 2018 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper functions for interacting with the binauthz API."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from apitools.base.py import list_pager
from googlecloudsdk.api_lib.util import apis

API_NAME = 'containeranalysis'

V1_ALPHA1 = 'v1alpha1'
V1_BETA1 = 'v1beta1'
V1 = 'v1'
DEFAULT_VERSION = V1


class Client(object):
  """A client to access containeranalysis for binauthz purposes."""

  def __init__(self, api_version=None):
    """Creates a ContainerAnalysisClient.

    Args:
      api_version: The containeranalysis API version to use.
    """
    self.api_version = api_version or DEFAULT_VERSION

    self.client = apis.GetClientInstance(API_NAME, self.api_version)
    self.messages = apis.GetMessagesModule(API_NAME, self.api_version)

  def YieldAttestations(self, note_ref, artifact_url=None):
    """Yields occurrences associated with given AA Note.

    Args:
      note_ref: The Note reference that will be queried for attached
        occurrences. (containeranalysis.projects.notes Resource)
      artifact_url: URL of the artifact for which to fetch occurrences. If None,
        then all occurrences attached to the AA Note are returned.

    Yields:
      Occurrences bound to `note_ref` with matching `artifact_url` (if passed).
    """
    occurrence_iter = list_pager.YieldFromList(
        self.client.projects_notes_occurrences,
        request=(
            self.messages.ContaineranalysisProjectsNotesOccurrencesListRequest(
                name=note_ref.RelativeName(),
                filter=('resourceUrl="{}"'.format(artifact_url)
                        if artifact_url is not None else ''),
            )),
        field='occurrences',
        batch_size=100,
        batch_size_attribute='pageSize',
    )

    # TODO(b/69380601): This should be handled by the filter parameter to
    # ListNoteOccurrences, but filtering isn't implemented yet for the fields
    # we care about.
    def MatchesFilter(occurrence):
      if (occurrence.kind !=
          self.messages.Occurrence.KindValueValuesEnum.ATTESTATION):
        return False
      return True

    for occurrence in occurrence_iter:
      if MatchesFilter(occurrence):
        yield occurrence

  def CreateAttestationOccurrence(self,
                                  note_ref,
                                  project_ref,
                                  artifact_url,
                                  public_key_id,
                                  signature,
                                  plaintext,
                                  validation_callback=None):
    """Creates an Occurrence referencing given URL and Note.

    This only supports the AttestationOccurrence-type Occurrence, currently only
    present in the v1 version of the API.

    Args:
      note_ref: The Note reference that the created Occurrence will be bound to.
        (containeranalysis.projects.notes Resource)
      project_ref: The project ref where the Occurrence will be created.
        (cloudresourcemanager.projects Resource)
      artifact_url: URL of artifact to which the signature is associated.
        (string)
      public_key_id: The URI of the public key that will be used to verify the
        signature. (string)
      signature: The content artifact's signature as generated by the specified
        key's signing operation. (string)
      plaintext: The content that was signed. (string)
      validation_callback: If provided, a function to validate the Occurrence
        prior to creation. (Callable[[Occurrence], None])

    Returns:
      Created Occurrence.
    """
    assert self.api_version == V1
    attestation = self.messages.AttestationOccurrence(
        serializedPayload=plaintext,
        signatures=[
            self.messages.Signature(
                publicKeyId=public_key_id, signature=signature),
        ],
    )
    occurrence = self.messages.Occurrence(
        kind=self.messages.Occurrence.KindValueValuesEnum.ATTESTATION,
        resourceUri=artifact_url,
        noteName=note_ref.RelativeName(),
        attestation=attestation,
    )
    request = self.messages.ContaineranalysisProjectsOccurrencesCreateRequest(
        parent=project_ref.RelativeName(),
        occurrence=occurrence,
    )

    if validation_callback is not None:
      validation_callback(occurrence)

    return self.client.projects_occurrences.Create(request)
