/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * 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 gitee_common

import (
	"context"
	"fmt"
	"fotff/res"
	"fotff/utils"
	"github.com/sirupsen/logrus"
	"os"
	"path/filepath"
)

type BuildConfig struct {
	Pkg           string
	PreCompileCMD string
	CompileCMD    string
	ImgList       []string
}

func (m *Manager) Build(config BuildConfig, ctx context.Context) error {
	if m.PkgAvailable(config) {
		return nil
	}
	logrus.Infof("%s is not available", config.Pkg)
	err := m.BuildNoRetry(config, false, ctx)
	if err == nil {
		return nil
	}
	logrus.Errorf("build pkg %s err: %v", config.Pkg, err)
	logrus.Infof("rm out and build pkg %s again...", config.Pkg)
	err = m.BuildNoRetry(config, true, ctx)
	if err == nil {
		return nil
	}
	logrus.Errorf("build pkg %s err: %v", config.Pkg, err)
	return err
}

// PkgAvailable returns true if all necessary images are all available to flash.
func (m *Manager) PkgAvailable(config BuildConfig) bool {
	for _, img := range config.ImgList {
		imgName := filepath.Base(img)
		if _, err := os.Stat(filepath.Join(m.Workspace, config.Pkg, imgName)); err != nil {
			return false
		}
	}
	return true
}

// BuildNoRetry obtain an available server, download corresponding codes, and run compile commands
// to build the corresponding package images, then transfer these images to the 'pkg' directory.
func (m *Manager) BuildNoRetry(config BuildConfig, rm bool, ctx context.Context) error {
	logrus.Infof("now Build %s", config.Pkg)
	server := res.GetBuildServer()
	defer res.ReleaseBuildServer(server)
	cmd := fmt.Sprintf("mkdir -p %s && cd %s && repo init -u https://gitee.com/openharmony/manifest.git", server.WorkSpace, server.WorkSpace)
	if err := utils.RunCmdViaSSHContext(ctx, server.Addr, server.User, server.Passwd, cmd); err != nil {
		return fmt.Errorf("remote: mkdir error: %w", err)
	}
	if err := utils.TransFileViaSSH(utils.Upload, server.Addr, server.User, server.Passwd,
		fmt.Sprintf("%s/.repo/manifest.xml", server.WorkSpace), filepath.Join(m.Workspace, config.Pkg, "manifest_tag.xml")); err != nil {
		return fmt.Errorf("upload and replace manifest error: %w", err)
	}
	// 'git lfs install' may fail due to some git hooks. Call 'git lfs update --force' before install to avoid this situation.
	cmd = fmt.Sprintf("cd %s && repo sync -c --no-tags --force-remove-dirty && repo forall -c 'git reset --hard && git clean -dfx && git lfs update --force && git lfs install && git lfs pull'", server.WorkSpace)
	if err := utils.RunCmdViaSSHContext(ctx, server.Addr, server.User, server.Passwd, cmd); err != nil {
		return fmt.Errorf("remote: repo sync error: %w", err)
	}
	cmd = fmt.Sprintf("cd %s && %s", server.WorkSpace, config.PreCompileCMD)
	if err := utils.RunCmdViaSSHContextNoRetry(ctx, server.Addr, server.User, server.Passwd, cmd); err != nil {
		return fmt.Errorf("remote: pre-compile command error: %w", err)
	}
	if rm {
		cmd = fmt.Sprintf("cd %s && rm -rf out", server.WorkSpace)
		if err := utils.RunCmdViaSSHContext(ctx, server.Addr, server.User, server.Passwd, cmd); err != nil {
			return fmt.Errorf("remote: rm ./out command error: %w", err)
		}
	}
	cmd = fmt.Sprintf("cd %s && %s", server.WorkSpace, config.CompileCMD)
	if err := utils.RunCmdViaSSHContextNoRetry(ctx, server.Addr, server.User, server.Passwd, cmd); err != nil {
		return fmt.Errorf("remote: compile command error: %w", err)
	}
	// has been built already, pitiful if canceled, so continue copying
	for _, f := range config.ImgList {
		imgName := filepath.Base(f)
		if err := utils.TransFileViaSSH(utils.Download, server.Addr, server.User, server.Passwd,
			fmt.Sprintf("%s/%s", server.WorkSpace, f), filepath.Join(m.Workspace, config.Pkg, imgName)); err != nil {
			return fmt.Errorf("download file %s error: %w", f, err)
		}
	}
	return nil
}