require "yaml"
require "json"
require "kubernetes"
require "./types"

module ContainerMom
  class SourceHandler
    class Error < Exception; end

    def self.handle(k8s, resource)
      new(k8s, resource).handle
    end

    def initialize(@k8s : K8sClientWrapper, @resource : Kubernetes::Resource(ContainerDeploymentSpec))
      @name = @resource.metadata.name
      @namespace = @resource.metadata.namespace
      @spec = @resource.spec
    end

    def handle
      case @spec.source.type
      when "registry"
        handle_registry_source
      when "git"
        handle_git_source
      else
        raise Error.new("Unknown source type: #{@spec.source.type}")
      end
    end

    private def handle_registry_source
      # For registry sources, we just need to verify the image exists
      # and create an ImagePullSecret if credentials are provided
      if credentials = @spec.source.credentials
        create_pull_secret(credentials)
      end

      # Return the image URL to use in deployments
      @spec.source.url
    end

    private def handle_git_source
      # For Git sources, we need to:
      # 1. Clone the repository
      # 2. Look for Dockerfile or docker-compose.yml
      # 3. Build the image
      # 4. Push to our registry
      # 5. Return the built image URL
      
      # Create a build ConfigMap to store build context
      create_build_config

      # Create a build Job
      job_name = "#{@name}-build"
      create_build_job(job_name)

      # Wait for build to complete
      wait_for_job(job_name)

      # Return the built image URL
      "container-mom.registry/#{@namespace}/#{@name}:latest"
    end

    private def create_pull_secret(credentials)
      auth = Base64.strict_encode("#{credentials.username}:#{credentials.password}")
      docker_config = {
        "auths" => {
          URI.parse(@spec.source.url).host => {
            "auth" => auth
          }
        }
      }

      @k8s.apply_secret(
        metadata: {
          name: "#{@name}-registry",
          namespace: @namespace,
        },
        type: "kubernetes.io/dockerconfigjson",
        data: {
          ".dockerconfigjson" => Base64.strict_encode(docker_config.to_json)
        }
      )
    end

    private def create_build_config
      # Create ConfigMap with build context
      @k8s.apply_configmap(
        metadata: {
          name: "#{@name}-build",
          namespace: @namespace,
        },
        data: {
          "repository" => @spec.source.url,
          "credentials" => @spec.source.credentials.try(&.to_json) || "{}",
        }
      )
    end

    private def create_build_job(job_name)
      @k8s.apply_job(
        metadata: {
          name: job_name,
          namespace: @namespace,
          labels: {
            "app.kubernetes.io/name" => @name,
            "app.kubernetes.io/component" => "build",
            "app.kubernetes.io/managed-by" => "container-mom",
          }
        },
        spec: {
          template: {
            spec: {
              containers: [{
                name: "builder",
                image: "kaniko-project/executor:latest",
                args: [
                  "--context=git://$(REPOSITORY)",
                  "--destination=container-mom.registry/$(NAMESPACE)/$(NAME):latest"
                ],
                env: [
                  {name: "REPOSITORY", value: @spec.source.url},
                  {name: "NAMESPACE", value: @namespace},
                  {name: "NAME", value: @name},
                ]
              }],
              restartPolicy: "Never"
            }
          },
          backoffLimit: 1
        }
      )
    end

    private def wait_for_job(job_name)
      loop do
        job = @k8s.get("/apis/batch/v1/namespaces/#{@namespace}/jobs/#{job_name}")
        if job.status["succeeded"]?
          break
        elsif job.status["failed"]?
          raise Error.new("Build job failed")
        end
        sleep 5
      end
    end
  end
end
