// Copyright 2016 bee authors
//
// 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.

package dockerize

import (
	"flag"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/beego/bee/v2/cmd/commands"
	"github.com/beego/bee/v2/cmd/commands/version"
	beeLogger "github.com/beego/bee/v2/logger"
	"github.com/beego/bee/v2/utils"
)

const dockerBuildTemplate = `# Build Golang binary
FROM {{.BaseImage}} AS build-golang

WORKDIR {{.Appdir}}

COPY . .
RUN go get -v && go build -v -o /usr/local/bin/{{.Entrypoint}}

EXPOSE {{.Expose}}
CMD ["{{.Entrypoint}}"]
`

const composeBuildTemplate = `version: '3'
networks:
  {{.Appname}}_network_compose:
    driver: bridge 
services:
  {{.Appname}}:
    container_name: {{.Appname}}
    build: .
    restart: unless-stopped
    networks:
      {{.Appname}}_network_compose:
    ports:{{.Expose}}
`

// Dockerfile holds the information about the Docker container.
type Dockerfile struct {
	BaseImage  string
	Appdir     string
	Entrypoint string
	Expose     string
}

// docker-compose.yaml
type Composefile struct {
	Appname   string
	Expose    string
}

var CmdDockerize = &commands.Command{
	CustomFlags: true,
	UsageLine:   "dockerize",
	Short:       "Generates a Dockerfile and docker-compose.yaml for your Beego application",
	Long: `Dockerize generates a Dockerfile and docker-compose.yaml for your Beego Web Application.
  The Dockerfile will compile and run the application.
  The docker-compose.yaml can be used to build and deploy the generated Dockerfile.
  {{"Example:"|bold}}
    $ bee dockerize -expose="3000,80,25"
  `,
	PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
	Run:    dockerizeApp,
}

var (
	expose    string
	baseImage string
)

func init() {
	fs := flag.NewFlagSet("dockerize", flag.ContinueOnError)
	fs.StringVar(&baseImage, "baseimage", "golang:1.20.2", "Set the base image of the Docker container.")
	fs.StringVar(&expose, "expose", "8080", "Port(s) to expose for the Docker container.")
	CmdDockerize.Flag = *fs
	commands.AvailableCommands = append(commands.AvailableCommands, CmdDockerize)
}

func dockerizeApp(cmd *commands.Command, args []string) int {
	if err := cmd.Flag.Parse(args); err != nil {
		beeLogger.Log.Fatalf("Error parsing flags: %v", err.Error())
	}

	beeLogger.Log.Info("Generating Dockerfile and docker-compose.yaml...")

	gopath := os.Getenv("GOPATH")
	dir, err := filepath.Abs(".")
	if err != nil {
		beeLogger.Log.Error(err.Error())
	}

	appdir := strings.Replace(dir, gopath, "", 1)

	// In case of multiple ports to expose inside the container,
	// replace all the commas with whitespaces.
	// See the verb EXPOSE in the Docker documentation.
	exposedockerfile := strings.Replace(expose, ",", " ", -1)
	
	// Multiple ports expose for docker-compose.yaml
        ports := strings.Fields(strings.Replace(expose, ",", " ", -1))
        exposecompose := ""
        for _, port := range ports {
            composeport := ("\n    - " + "\"" + port + ":" + port + "\"")
            exposecompose += composeport
        }
	
	
	_, entrypoint := path.Split(appdir)
	dockerfile := Dockerfile{
		BaseImage:  baseImage,
		Appdir:     appdir,
		Entrypoint: entrypoint,
		Expose:     exposedockerfile,
	}
	composefile := Composefile{
		Appname:    entrypoint,
		Expose:     exposecompose,
	}

	generateDockerfile(dockerfile)
	generatecomposefile(composefile)
	return 0
}

func generateDockerfile(df Dockerfile) {
	t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(utils.BeeFuncMap())

	f, err := os.Create("Dockerfile")
	if err != nil {
		beeLogger.Log.Fatalf("Error writing Dockerfile: %v", err.Error())
	}
	defer utils.CloseFile(f)

	t.Execute(f, df)

	beeLogger.Log.Success("Dockerfile generated.")
}

func generatecomposefile(df Composefile) {
	t := template.Must(template.New("composeBuildTemplate").Parse(composeBuildTemplate)).Funcs(utils.BeeFuncMap())

	f, err := os.Create("docker-compose.yaml")
	if err != nil {
		beeLogger.Log.Fatalf("Error writing docker-compose.yaml: %v", err.Error())
	}
	defer utils.CloseFile(f)

	t.Execute(f, df)

	beeLogger.Log.Success("docker-compose.yaml generated.")
}
