# frozen_string_literal: true

module KubernetesHelpers
  include Gitlab::Kubernetes

  def kube_response(body)
    { body: body.to_json }
  end

  def kube_pods_response
    kube_response(kube_pods_body)
  end

  def kube_pod_response
    kube_response(kube_pod)
  end

  def kube_logs_response
    { body: kube_logs_body }
  end

  def kube_deployments_response
    kube_response(kube_deployments_body)
  end

  def stub_kubeclient_discover_base(api_url)
    WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body))
    WebMock
      .stub_request(:get, api_url + '/apis/extensions/v1beta1')
      .to_return(kube_response(kube_v1beta1_discovery_body))
    WebMock
      .stub_request(:get, api_url + '/apis/rbac.authorization.k8s.io/v1')
      .to_return(kube_response(kube_v1_rbac_authorization_discovery_body))
  end

  def stub_kubeclient_discover(api_url)
    stub_kubeclient_discover_base(api_url)

    WebMock
      .stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1')
      .to_return(kube_response(kube_v1alpha1_serving_knative_discovery_body))
  end

  def stub_kubeclient_discover_knative_not_found(api_url)
    stub_kubeclient_discover_base(api_url)

    WebMock
      .stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1')
      .to_return(status: [404, "Resource Not Found"])
  end

  def stub_kubeclient_service_pods(response = nil, options = {})
    stub_kubeclient_discover(service.api_url)

    namespace_path = options[:namespace].present? ? "namespaces/#{options[:namespace]}/" : ""

    pods_url = service.api_url + "/api/v1/#{namespace_path}pods"

    WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response)
  end

  def stub_kubeclient_pods(namespace, status: nil)
    stub_kubeclient_discover(service.api_url)
    pods_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods"
    response = { status: status } if status

    WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response)
  end

  def stub_kubeclient_pod_details(pod, namespace, status: nil)
    stub_kubeclient_discover(service.api_url)

    pod_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods/#{pod}"
    response = { status: status } if status

    WebMock.stub_request(:get, pod_url).to_return(response || kube_pod_response)
  end

  def stub_kubeclient_logs(pod_name, namespace, container: nil, status: nil, message: nil)
    stub_kubeclient_discover(service.api_url)

    if container
      container_query_param = "container=#{container}&"
    end

    logs_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods/#{pod_name}" \
    "/log?#{container_query_param}tailLines=#{Clusters::Platforms::Kubernetes::LOGS_LIMIT}"

    if status
      response = { status: status }
      response[:body] = { message: message }.to_json if message
    end

    WebMock.stub_request(:get, logs_url).to_return(response || kube_logs_response)
  end

  def stub_kubeclient_deployments(namespace, status: nil)
    stub_kubeclient_discover(service.api_url)
    deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{namespace}/deployments"
    response = { status: status } if status

    WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
  end

  def stub_kubeclient_knative_services(options = {})
    namespace_path = options[:namespace].present? ? "namespaces/#{options[:namespace]}/" : ""

    options[:name] ||= "kubetest"
    options[:domain] ||= "example.com"
    options[:response] ||= kube_response(kube_knative_services_body(options))

    stub_kubeclient_discover(service.api_url)

    knative_url = service.api_url + "/apis/serving.knative.dev/v1alpha1/#{namespace_path}services"

    WebMock.stub_request(:get, knative_url).to_return(options[:response])
  end

  def stub_kubeclient_get_secret(api_url, **options)
    options[:metadata_name] ||= "default-token-1"
    options[:namespace] ||= "default"

    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
      .to_return(kube_response(kube_v1_secret_body(options)))
  end

  def stub_kubeclient_get_secret_error(api_url, name, namespace: 'default', status: 404)
    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}")
      .to_return(status: [status, "Internal Server Error"])
  end

  def stub_kubeclient_get_secret_not_found_then_found(api_url, **options)
    options[:metadata_name] ||= "default-token-1"
    options[:namespace] ||= "default"

    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
      .to_return(status: [404, "Not Found"])
      .then
      .to_return(kube_response(kube_v1_secret_body(options)))
  end

  def stub_kubeclient_get_secret_missing_token_then_with_token(api_url, **options)
    options[:metadata_name] ||= "default-token-1"
    options[:namespace] ||= "default"

    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
      .to_return(kube_response(kube_v1_secret_body(options.merge(token: nil))))
      .then
      .to_return(kube_response(kube_v1_secret_body(options)))
  end

  def stub_kubeclient_get_service_account(api_url, name, namespace: 'default')
    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_get_service_account_error(api_url, name, namespace: 'default', status: 404)
    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}")
      .to_return(status: [status, "Internal Server Error"])
  end

  def stub_kubeclient_create_service_account(api_url, namespace: 'default')
    WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_create_service_account_error(api_url, namespace: 'default')
    WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts")
      .to_return(status: [500, "Internal Server Error"])
  end

  def stub_kubeclient_put_service_account(api_url, name, namespace: 'default')
    WebMock.stub_request(:put, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_create_secret(api_url, namespace: 'default')
    WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_put_secret(api_url, name, namespace: 'default')
    WebMock.stub_request(:put, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_get_cluster_role_binding_error(api_url, name, status: 404)
    WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/#{name}")
      .to_return(status: [status, "Internal Server Error"])
  end

  def stub_kubeclient_create_cluster_role_binding(api_url)
    WebMock.stub_request(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings')
      .to_return(kube_response({}))
  end

  def stub_kubeclient_get_role_binding(api_url, name, namespace: 'default')
    WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_get_role_binding_error(api_url, name, namespace: 'default', status: 404)
    WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
      .to_return(status: [status, "Internal Server Error"])
  end

  def stub_kubeclient_create_role_binding(api_url, namespace: 'default')
    WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_put_role_binding(api_url, name, namespace: 'default')
    WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_create_namespace(api_url)
    WebMock.stub_request(:post, api_url + "/api/v1/namespaces")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_get_namespace(api_url, namespace: 'default')
    WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}")
      .to_return(kube_response({}))
  end

  def stub_kubeclient_put_role(api_url, name, namespace: 'default')
    WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{name}")
      .to_return(kube_response({}))
  end

  def kube_v1_secret_body(**options)
    {
      "kind" => "SecretList",
      "apiVersion": "v1",
      "metadata": {
        "name": options.fetch(:metadata_name, "default-token-1"),
        "namespace": "kube-system"
      },
      "data": {
        "token": options.fetch(:token, Base64.encode64('token-sample-123'))
      }
    }
  end

  def kube_v1_discovery_body
    {
      "kind" => "APIResourceList",
      "resources" => [
        { "name" => "pods", "namespaced" => true, "kind" => "Pod" },
        { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
        { "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
        { "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
        { "name" => "services", "namespaced" => true, "kind" => "Service" },
        { "name" => "namespaces", "namespaced" => true, "kind" => "Namespace" }
      ]
    }
  end

  def kube_v1beta1_discovery_body
    {
      "kind" => "APIResourceList",
      "resources" => [
        { "name" => "pods", "namespaced" => true, "kind" => "Pod" },
        { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
        { "name" => "secrets", "namespaced" => true, "kind" => "Secret" },
        { "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" },
        { "name" => "services", "namespaced" => true, "kind" => "Service" }
      ]
    }
  end

  def kube_v1_rbac_authorization_discovery_body
    {
      "kind" => "APIResourceList",
      "resources" => [
        { "name" => "clusterrolebindings", "namespaced" => false, "kind" => "ClusterRoleBinding" },
        { "name" => "clusterroles", "namespaced" => false, "kind" => "ClusterRole" },
        { "name" => "rolebindings", "namespaced" => true, "kind" => "RoleBinding" },
        { "name" => "roles", "namespaced" => true, "kind" => "Role" }
      ]
    }
  end

  def kube_v1alpha1_serving_knative_discovery_body
    {
      "kind" => "APIResourceList",
      "resources" => [
        { "name" => "revisions", "namespaced" => true, "kind" => "Revision" },
        { "name" => "services", "namespaced" => true, "kind" => "Service" },
        { "name" => "configurations", "namespaced" => true, "kind" => "Configuration" },
        { "name" => "routes", "namespaced" => true, "kind" => "Route" }
      ]
    }
  end

  def kube_pods_body
    {
      "kind" => "PodList",
      "items" => [kube_pod]
    }
  end

  def kube_logs_body
    "Log 1\nLog 2\nLog 3"
  end

  def kube_deployments_body
    {
      "kind" => "DeploymentList",
      "items" => [kube_deployment]
    }
  end

  def kube_knative_pods_body(name, namespace)
    {
      "kind" => "PodList",
      "items" => [kube_knative_pod(name: name, namespace: namespace)]
    }
  end

  def kube_knative_services_body(**options)
    {
      "kind" => "List",
      "items" => [knative_07_service(options)]
    }
  end

  # This is a partial response, it will have many more elements in reality but
  # these are the ones we care about at the moment
  def kube_pod(name: "kube-pod", environment_slug: "production", namespace: "project-namespace", project_slug: "project-path-slug", status: "Running", track: nil)
    {
      "metadata" => {
        "name" => name,
        "namespace" => namespace,
        "generate_name" => "generated-name-with-suffix",
        "creationTimestamp" => "2016-11-25T19:55:19Z",
        "annotations" => {
          "app.gitlab.com/env" => environment_slug,
          "app.gitlab.com/app" => project_slug
        },
        "labels" => {
          "track" => track
        }.compact
      },
      "spec" => {
        "containers" => [
          { "name" => "container-0" },
          { "name" => "container-1" }
        ]
      },
      "status" => { "phase" => status }
    }
  end

  # Similar to a kube_pod, but should contain a running service
  def kube_knative_pod(name: "kube-pod", namespace: "default", status: "Running")
    {
      "metadata" => {
        "name" => name,
        "namespace" => namespace,
        "generate_name" => "generated-name-with-suffix",
        "creationTimestamp" => "2016-11-25T19:55:19Z",
        "labels" => {
          "serving.knative.dev/service" => name
        }
      },
      "spec" => {
        "containers" => [
          { "name" => "container-0" },
          { "name" => "container-1" }
        ]
      },
      "status" => { "phase" => status }
    }
  end

  def kube_deployment(name: "kube-deployment", environment_slug: "production", project_slug: "project-path-slug", track: nil)
    {
      "metadata" => {
        "name" => name,
        "generation" => 4,
        "annotations" => {
          "app.gitlab.com/env" => environment_slug,
          "app.gitlab.com/app" => project_slug
        },
        "labels" => {
          "track" => track
        }.compact
      },
      "spec" => { "replicas" => 3 },
      "status" => {
        "observedGeneration" => 4,
        "replicas" => 3,
        "updatedReplicas" => 3,
        "availableReplicas" => 3
      }
    }
  end

  # noinspection RubyStringKeysInHashInspection
  def knative_06_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production')
    { "apiVersion" => "serving.knative.dev/v1alpha1",
      "kind" => "Service",
      "metadata" =>
        { "annotations" =>
            { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account",
              "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" },
          "creationTimestamp" => "2019-10-22T21:19:20Z",
          "generation" => 1,
          "labels" => { "service" => name },
          "name" => name,
          "namespace" => namespace,
          "resourceVersion" => "6042",
          "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}",
          "uid" => "9c7f63d0-f511-11e9-8815-42010a80002f" },
      "spec" => {
        "runLatest" => {
          "configuration" => {
            "revisionTemplate" => {
              "metadata" => {
                "annotations" => { "Description" => description },
                "creationTimestamp" => "2019-10-22T21:19:20Z",
                "labels" => { "service" => name }
              },
              "spec" => {
                "container" => {
                  "env" => [{ "name" => "timestamp", "value" => "2019-10-22 21:19:20" }],
                  "image" => "image_name",
                  "name" => "",
                  "resources" => {}
                },
                "timeoutSeconds" => 300
              }
            }
          }
        }
      },
      "status" => {
        "address" => {
          "hostname" => "#{name}.#{namespace}.svc.cluster.local",
          "url" => "http://#{name}.#{namespace}.svc.cluster.local"
        },
        "conditions" =>
          [{ "lastTransitionTime" => "2019-10-22T21:20:25Z", "status" => "True", "type" => "ConfigurationsReady" },
           { "lastTransitionTime" => "2019-10-22T21:20:25Z", "status" => "True", "type" => "Ready" },
           { "lastTransitionTime" => "2019-10-22T21:20:25Z", "status" => "True", "type" => "RoutesReady" }],
        "domain" => "#{name}.#{namespace}.#{domain}",
        "domainInternal" => "#{name}.#{namespace}.svc.cluster.local",
        "latestCreatedRevisionName" => "#{name}-bskx6",
        "latestReadyRevisionName" => "#{name}-bskx6",
        "observedGeneration" => 1,
        "traffic" => [{ "latestRevision" => true, "percent" => 100, "revisionName" => "#{name}-bskx6" }],
        "url" => "http://#{name}.#{namespace}.#{domain}"
      },
      "environment_scope" => environment,
      "cluster_id" => 9,
      "podcount" => 0 }
  end

  # noinspection RubyStringKeysInHashInspection
  def knative_07_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production')
    { "apiVersion" => "serving.knative.dev/v1alpha1",
      "kind" => "Service",
      "metadata" =>
        { "annotations" =>
            { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account",
              "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" },
          "creationTimestamp" => "2019-10-22T21:19:13Z",
          "generation" => 1,
          "labels" => { "service" => name },
          "name" => name,
          "namespace" => namespace,
          "resourceVersion" => "289726",
          "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}",
          "uid" => "988349fa-f511-11e9-9ea1-42010a80005e" },
      "spec" => {
        "template" => {
          "metadata" => {
            "annotations" => { "Description" => description },
            "creationTimestamp" => "2019-10-22T21:19:12Z",
            "labels" => { "service" => name }
          },
          "spec" => {
            "containers" => [{
                               "env" =>
                                 [{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }],
                               "image" => "image_name",
                               "name" => "user-container",
                               "resources" => {}
                             }],
            "timeoutSeconds" => 300
          }
        },
        "traffic" => [{ "latestRevision" => true, "percent" => 100 }]
      },
      "status" =>
        { "address" => { "url" => "http://#{name}.#{namespace}.svc.cluster.local" },
          "conditions" =>
            [{ "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "ConfigurationsReady" },
             { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "Ready" },
             { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "RoutesReady" }],
          "latestCreatedRevisionName" => "#{name}-92tsj",
          "latestReadyRevisionName" => "#{name}-92tsj",
          "observedGeneration" => 1,
          "traffic" => [{ "latestRevision" => true, "percent" => 100, "revisionName" => "#{name}-92tsj" }],
          "url" => "http://#{name}.#{namespace}.#{domain}" },
      "environment_scope" => environment,
      "cluster_id" => 5,
      "podcount" => 0 }
  end

  # noinspection RubyStringKeysInHashInspection
  def knative_05_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production')
    { "apiVersion" => "serving.knative.dev/v1alpha1",
      "kind" => "Service",
      "metadata" =>
        { "annotations" =>
            { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account",
              "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" },
          "creationTimestamp" => "2019-10-22T21:19:19Z",
          "generation" => 1,
          "labels" => { "service" => name },
          "name" => name,
          "namespace" => namespace,
          "resourceVersion" => "330390",
          "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}",
          "uid" => "9c710da6-f511-11e9-9ba0-42010a800161" },
      "spec" => {
        "runLatest" => {
          "configuration" => {
            "revisionTemplate" => {
              "metadata" => {
                "annotations" => { "Description" => description },
                "creationTimestamp" => "2019-10-22T21:19:19Z",
                "labels" => { "service" => name }
              },
              "spec" => {
                "container" => {
                  "env" => [{ "name" => "timestamp", "value" => "2019-10-22 21:19:19" }],
                  "image" => "image_name",
                  "name" => "",
                  "resources" => { "requests" => { "cpu" => "400m" } }
                },
                "timeoutSeconds" => 300
              }
            }
          }
        }
      },
      "status" =>
        { "address" => { "hostname" => "#{name}.#{namespace}.svc.cluster.local" },
          "conditions" =>
            [{ "lastTransitionTime" => "2019-10-22T21:20:24Z", "status" => "True", "type" => "ConfigurationsReady" },
             { "lastTransitionTime" => "2019-10-22T21:20:24Z", "status" => "True", "type" => "Ready" },
             { "lastTransitionTime" => "2019-10-22T21:20:24Z", "status" => "True", "type" => "RoutesReady" }],
          "domain" => "#{name}.#{namespace}.#{domain}",
          "domainInternal" => "#{name}.#{namespace}.svc.cluster.local",
          "latestCreatedRevisionName" => "#{name}-58qgr",
          "latestReadyRevisionName" => "#{name}-58qgr",
          "observedGeneration" => 1,
          "traffic" => [{ "percent" => 100, "revisionName" => "#{name}-58qgr" }] },
      "environment_scope" => environment,
      "cluster_id" => 8,
      "podcount" => 0 }
  end

  def kube_terminals(service, pod)
    pod_name = pod['metadata']['name']
    pod_namespace = pod['metadata']['namespace']
    containers = pod['spec']['containers']

    containers.map do |container|
      terminal = {
        selectors: { pod: pod_name, container: container['name'] },
        url:  container_exec_url(service.api_url, pod_namespace, pod_name, container['name']),
        subprotocols: ['channel.k8s.io'],
        headers: { 'Authorization' => ["Bearer #{service.token}"] },
        created_at: DateTime.parse(pod['metadata']['creationTimestamp']),
        max_session_time: 0
      }
      terminal[:ca_pem] = service.ca_pem if service.ca_pem.present?
      terminal
    end
  end

  def kube_deployment_rollout_status
    ::Gitlab::Kubernetes::RolloutStatus.from_deployments(kube_deployment)
  end

  def empty_deployment_rollout_status
    ::Gitlab::Kubernetes::RolloutStatus.from_deployments
  end
end