package app

import (
	"context"
	"fmt"
	"io"
	"os"
	"sync"

	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/pkg/autocomplete"
	"coopcloud.tech/abra/pkg/client"
	"coopcloud.tech/abra/pkg/config"
	"coopcloud.tech/abra/pkg/runtime"
	"coopcloud.tech/abra/pkg/service"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	dockerClient "github.com/docker/docker/client"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

var logOpts = types.ContainerLogsOptions{
	ShowStderr: true,
	ShowStdout: true,
	Since:      "",
	Until:      "",
	Timestamps: true,
	Follow:     true,
	Tail:       "20",
	Details:    false,
}

// stackLogs lists logs for all stack services
func stackLogs(c *cli.Context, app config.App, client *dockerClient.Client) {
	filters, err := app.Filters(true, false)
	if err != nil {
		logrus.Fatal(err)
	}

	serviceOpts := types.ServiceListOptions{Filters: filters}
	services, err := client.ServiceList(context.Background(), serviceOpts)
	if err != nil {
		logrus.Fatal(err)
	}

	var wg sync.WaitGroup
	for _, service := range services {
		wg.Add(1)
		go func(s string) {
			if internal.StdErrOnly {
				logOpts.ShowStdout = false
			}

			logs, err := client.ServiceLogs(context.Background(), s, logOpts)
			if err != nil {
				logrus.Fatal(err)
			}
			defer logs.Close()

			_, err = io.Copy(os.Stdout, logs)
			if err != nil && err != io.EOF {
				logrus.Fatal(err)
			}
		}(service.ID)
	}

	wg.Wait()

	os.Exit(0)
}

var appLogsCommand = cli.Command{
	Name:      "logs",
	Aliases:   []string{"l"},
	ArgsUsage: "<domain> [<service>]",
	Usage:     "Tail app logs",
	Flags: []cli.Flag{
		internal.StdErrOnlyFlag,
		internal.SinceLogsFlag,
		internal.DebugFlag,
	},
	Before:       internal.SubCommandBefore,
	BashComplete: autocomplete.AppNameComplete,
	Action: func(c *cli.Context) error {
		app := internal.ValidateApp(c, runtime.WithEnsureRecipeExists(false))

		cl, err := client.New(app.Server)
		if err != nil {
			logrus.Fatal(err)
		}

		logOpts.Since = internal.SinceLogs

		serviceName := c.Args().Get(1)
		if serviceName == "" {
			logrus.Debugf("tailing logs for all %s services", app.Recipe)
			stackLogs(c, app, cl)
		} else {
			logrus.Debugf("tailing logs for %s", serviceName)
			if err := tailServiceLogs(c, cl, app, serviceName); err != nil {
				logrus.Fatal(err)
			}
		}

		return nil
	},
}

func tailServiceLogs(c *cli.Context, cl *dockerClient.Client, app config.App, serviceName string) error {
	filters := filters.NewArgs()
	filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))

	chosenService, err := service.GetService(context.Background(), cl, filters, internal.NoInput)
	if err != nil {
		logrus.Fatal(err)
	}

	if internal.StdErrOnly {
		logOpts.ShowStdout = false
	}

	logs, err := cl.ServiceLogs(context.Background(), chosenService.ID, logOpts)
	if err != nil {
		logrus.Fatal(err)
	}
	defer logs.Close()

	_, err = io.Copy(os.Stdout, logs)
	if err != nil && err != io.EOF {
		logrus.Fatal(err)
	}

	return nil
}