# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Utilities for the parsing ouput for cloud build v2 API."""
from apitools.base.py import encoding
from googlecloudsdk.api_lib.cloudbuild import cloudbuild_util
from googlecloudsdk.api_lib.cloudbuild.v2 import output_util
from googlecloudsdk.core import yaml
from googlecloudsdk.core.resource import custom_printer_base


PRINTER_FORMAT = "tekton"


class TektonPrinter(custom_printer_base.CustomPrinterBase):
  """Print a  PipelineRun or TaskRun in Tekton YAML format."""

  def Transform(self, internal_proto):
    proto = encoding.MessageToDict(internal_proto)
    if (
        "pipelineSpec" in proto
        or "pipelineRef" in proto
        or "pipelineSpecYaml" in proto
    ):
      yaml_str = self.PublicPRToTektonPR(proto)
      return yaml.dump(yaml_str, round_trip=True)
    elif "taskSpec" in proto or "taskRef" in proto:
      yaml_str = self.PublicTRToTektonPR(proto)
      return yaml.dump(yaml_str, round_trip=True)

  def PublicPRToTektonPR(self, internal):
    """Convert a PipelineRun message into Tekton yaml."""
    pr = {
        "metadata": {},
        "spec": {},
        "status": {},
    }
    # METADATA
    if "name" in internal:
      pr["metadata"]["name"] = output_util.ParseName(
          internal.pop("name"), "pipelinerun"
      )
    if "annotations" in internal:
      pr["metadata"]["annotations"] = internal.pop("annotations")
    # SPEC
    if "params" in internal:
      pr["spec"]["params"] = _TransformParams(internal.pop("params"))
    if "pipelineSpec" in internal:
      pr["spec"]["pipelineSpec"] = _TransformPipelineSpec(
          internal.pop("pipelineSpec")
      )
    elif "pipelineRef" in internal:
      pr["spec"]["pipelineRef"] = TransformRef(internal.pop("pipelineRef"))
    elif "pipelineSpecYaml" in internal:
      yaml_string = internal.pop("pipelineSpecYaml")
      formatted_yaml = yaml.load(yaml_string, round_trip=True)
      pr["spec"]["pipelineSpecYaml"] = formatted_yaml
    if "timeout" in internal:
      pr["spec"]["timeout"] = internal.pop("timeout")
    if "workspaces" in internal:
      pr["spec"]["workspaces"] = internal.pop("workspaces")
    # STATUS
    if "conditions" in internal:
      conditions = internal.pop("conditions")
      pr["status"]["conditions"] = _TransformConditions(conditions)
    if "startTime" in internal:
      pr["status"]["startTime"] = internal.pop("startTime")
    if "completionTime" in internal:
      pr["status"]["completionTime"] = internal.pop("completionTime")
    # We set the resolvedPipelineSpec as status in Tekton
    if "resolvedPipelineSpec" in internal:
      rps = internal.pop("resolvedPipelineSpec")
      pr["status"]["pipelineSpec"] = _TransformPipelineSpec(rps)
    # PipelineRunResults
    if "results" in internal:
      pr["status"]["results"] = _TransformPipelineRunResults(
          internal.pop("results")
      )
    if "childReferences" in internal:
      crs = internal.pop("childReferences")
      pr["status"]["childReferences"] = crs
    # TASKRUNTEMPLATE
    if "serviceAccount" in internal:
      pr["taskRunTemplate"] = {
          "serviceAccountName": internal.pop("serviceAccount"),
      }
    return pr

  def PublicTRToTektonPR(self, internal):
    """Convert a TaskRun message into Tekton yaml."""
    tr = {
        "metadata": {},
        "spec": {},
        "status": {},
    }
    # METADATA
    if "name" in internal:
      tr["metadata"]["name"] = output_util.ParseName(
          internal.pop("name"), "taskrun"
      )
    # SPEC
    if "params" in internal:
      tr["spec"]["params"] = _TransformParams(internal.pop("params"))
    if "taskSpec" in internal:
      tr["spec"]["taskSpec"] = _TransformTaskSpec(internal.pop("taskSpec"))
    elif "taskRef" in internal:
      tr["spec"]["taskRef"] = TransformRef(internal.pop("taskRef"))
    if "timeout" in internal:
      tr["spec"]["timeout"] = internal.pop("timeout")
    if "workspaces" in internal:
      tr["spec"]["workspaces"] = internal.pop("workspaces")
    if "serviceAccountName" in internal:
      tr["spec"]["serviceAccountName"] = internal.pop("serviceAccountName")
    # STATUS
    if "conditions" in internal:
      tr["status"]["conditions"] = _TransformConditions(
          internal.pop("conditions")
      )
    if "startTime" in internal:
      tr["status"]["startTime"] = internal.pop("startTime")
    if "completionTime" in internal:
      tr["status"]["completionTime"] = internal.pop("completionTime")
    # We set the resolvedTaskSpec as the Status field in Tekton
    if "resolvedTaskSpec" in internal:
      rts = internal.pop("resolvedTaskSpec")
      tr["status"]["taskSpec"] = _TransformTaskSpec(rts)
    # StepState
    if "steps" in internal:
      tr["status"]["steps"] = _TransformStepStates(internal.pop("steps"))
    # TaskRunResults
    if "results" in internal:
      tr["status"]["results"] = _TransformTaskRunResults(
          internal.pop("results")
      )
    # SidecarState
    if "sidecars" in internal:
      tr["status"]["sidecars"] = internal.pop("sidecars")
    return tr


def _TransformPipelineSpec(ps):
  """Convert PipelineSpec into Tekton yaml."""
  pipeline_spec = {}
  if "params" in ps:
    pipeline_spec["params"] = TransformParamsSpec(ps.pop("params"))
  if "tasks" in ps:
    pipeline_spec["tasks"] = _TransformPipelineTasks(ps.pop("tasks"))
  if "results" in ps:
    pipeline_spec["results"] = _TransformPipelineResults(ps.pop("results"))
  if "finallyTasks" in ps:
    pipeline_spec["finally"] = _TransformPipelineTasks(ps.pop("finallyTasks"))
  if "workspaces" in ps:
    pipeline_spec["workspaces"] = ps.pop("workspaces")
  return pipeline_spec


def TransformParamsSpec(ps):
  """Convert ParamsSpecs into Tekton yaml."""
  param_spec = []
  for p in ps:
    param = {}
    if "name" in p:
      param["name"] = p.pop("name")
    if "description" in p:
      param["description"] = p.pop("description")
    if "type" in p:
      param["type"] = p.pop("type").lower()
    if "default" in p:
      param["default"] = _TransformParamValue(p.pop("default"))
    if "properties" in p:
      param["properties"] = p.pop("properties")
    param_spec.append(param)
  return param_spec


def _TransformTaskSpec(ts):
  """Convert TaskSpecs into Tekton yaml."""
  task_spec = {}
  if "params" in ts:
    task_spec["params"] = TransformParamsSpec(ts.pop("params"))
  if "steps" in ts:
    task_spec["steps"] = _TransformSteps(ts.pop("steps"))
  if "stepTemplate" in ts:
    task_spec["stepTemplate"] = ts.pop("stepTemplate")
  if "results" in ts:
    task_spec["results"] = _TransformTaskResults(ts.pop("results"))
  if "sidecars" in ts:
    task_spec["sidecars"] = ts.pop("sidecars")
  if "workspaces" in ts:
    task_spec["workspaces"] = ts.pop("workspaces")
  return task_spec


def _TransformOnError(oe):
  """Convert OnError into Tekton yaml."""
  return cloudbuild_util.SnakeToCamelString(oe.lower())


def _TransformSteps(steps):
  """Convert Steps into Tekton yaml."""
  results = []
  for step in steps:
    if "ref" in step:
      step["ref"] = TransformRef(step.pop("ref"))
    if "params" in step:
      step["params"] = _TransformParams(step.pop("params"))
    results.append(step)
    if "onError" in step:
      step["onError"] = _TransformOnError(step.pop("onError"))
  return results


def _TransformPipelineTasks(ts):
  """Convert PipelineTasks into Tekton yaml."""
  tasks = []
  for task in ts:
    t = {"name": task.get("name", None)}
    if "params" in task:
      t["params"] = _TransformParams(task.pop("params"))
    if "taskSpec" in task:
      task_spec = task.pop("taskSpec").pop("taskSpec")
      t["taskSpec"] = _TransformTaskSpec(task_spec)
    elif "taskRef" in task:
      t["taskRef"] = task.pop("taskRef")
    if "workspaces" in task:
      t["workspaces"] = task.pop("workspaces")
    if "runAfter" in task:
      t["runAfter"] = task.pop("runAfter")
    if "timeout" in task:
      t["timeout"] = task.pop("timeout")
    tasks.append(t)
  return tasks


def _TransformPipelineResults(rs):
  """Convert PipelineResults into Tekton yaml."""
  results = []
  for r in rs:
    result = {}
    if "name" in r:
      result["name"] = r.pop("name")
    if "description" in r:
      result["description"] = r.pop("description")
    if "type" in r:
      result["type"] = r.pop("type").lower()
    if "value" in r:
      result["value"] = _TransformResultValue(r.pop("value"))
    results.append(result)
  return results


def _TransformTaskResults(rs):
  """Convert TaskResults into Tekton yaml."""
  results = []
  for r in rs:
    result = {}
    if "name" in r:
      result["name"] = r.pop("name")
    if "description" in r:
      result["description"] = r.pop("description")
    if "type" in r:
      result["type"] = r.pop("type").lower()
    if "properties" in r:
      result["properties"] = r.pop("properties")
    if "value" in r:
      result["value"] = _TransformParamValue(r.pop("value"))
    results.append(result)
  return results


def _TransformPipelineRunResults(rs):
  """Convert PipelineRunResults into Tekton yaml."""
  results = []
  for r in rs:
    result = {}
    if "name" in r:
      result["name"] = r.pop("name")
    if "value" in r:
      result["value"] = _TransformResultValue(r.pop("value"))
    results.append(result)
  return results


def _TransformStepStates(steps):
  """Convert StepState into Tekton yaml."""
  step_states = []
  for s in steps:
    if "results" in s:
      s["results"] = _TransformTaskRunResults(s.pop("results"))
    step_states.append(s)
  return step_states


def _TransformTaskRunResults(rs):
  """Convert TaskRunResults into Tekton yaml."""
  results = []
  for r in rs:
    result = {}
    if "name" in r:
      result["name"] = r.pop("name")
    if "resultValue" in r:
      result["value"] = _TransformResultValue(r.pop("resultValue"))
    results.append(result)
  return results


def _TransformResultValue(v):
  """Convert ResultValue into Tekton yaml."""
  if "stringVal" in v:
    return v.pop("stringVal")
  if "arrayVal" in v:
    return v.pop("arrayVal")
  if "objectVal" in v:
    return v.pop("objectVal")
  return v


def _TransformParamValue(v):
  """Convert ParamValue into Tekton yaml."""
  if "stringVal" in v:
    return v.pop("stringVal")
  if "arrayVal" in v:
    return v.pop("arrayVal")
  return v


def _TransformParams(ps):
  """Convert Params into Tekton yaml."""
  params = []
  for p in ps:
    param = {}
    if "name" in p:
      param["name"] = p.pop("name")
    if "value" in p:
      param["value"] = _TransformParamValue(p.pop("value"))
    params.append(param)
  return params


def _TransformConditions(cs):
  """Convert Conditions into Tekton yaml."""
  conditions = []
  for c in cs:
    condition = {}
    # Only append the condition if it has a message
    # which indicates the final condition
    if "message" in c:
      condition["message"] = c.pop("message")
      if "lastTransitionTime" in c:
        condition["lastTransitionTime"] = c.pop("lastTransitionTime")
      if "status" in c:
        condition["status"] = c.pop("status").capitalize()
      if "type" in c:
        condition["type"] = c.pop("type").capitalize()
      if "reason" in c:
        condition["reason"] = c.pop("reason")
        conditions.append(condition)
  return conditions


def _TransformChildRefs(crs):
  """Convert ChildReferences into Tekton yaml."""
  child_refs = []
  for cr in crs:
    child_ref = {}
    if "name" in cr:
      child_ref["name"] = cr.pop("name")
    if "pipelineTask" in cr:
      child_ref["pipelineTask"] = cr.pop("pipelineTask")
    child_refs.append(child_ref)
  return child_refs


def TransformRef(ref):
  """Convert a generic reference (step, task, or pipeline) into Tekton yaml."""
  result = {}
  if "name" in ref:
    result["name"] = ref.pop("name")
  if "resolver" in ref:
    result["resolver"] = ref.pop("resolver")
  if "params" in ref:
    result["params"] = _TransformParams(ref.pop("params"))
  return result
