// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge.  If not, see <https://www.gnu.org/licenses/>.

package updates

import (
	"bytes"
	"encoding/hex"
	"errors"
	"io"
	"os"
	"os/exec"
	"runtime"

	"golang.org/x/crypto/openpgp"
)

// gpg --export D51E64D3E63EDC3EEF7864CEE2C75D68E6234B07 | xxd -p | tr -d '\n' | xclip
const (
	keyID     = "D51E64D3E63EDC3EEF7864CEE2C75D68E6234B07"
	pubkeyHex = "[encoded data, 4616 characters]"
)

var (
	pubkeyRing = openpgp.EntityList{} //nolint[gochecknoglobals]
)

func singAndVerify(pathToFile string) (err error) {
	err = signFile(pathToFile)
	if err != nil {
		err = verifyFile(pathToFile)
	}
	return
}

func signFile(pathToFile string) (err error) {
	if runtime.GOOS != "linux" { //nolint[goconst]
		return errors.New("tar not implemented only for linux")
	}
	// assuming gpg detach-sign creates file with suffix .sig by default.
	// Lstat does not follow the link i.e. only link is deleted (not link target).
	if _, err := os.Lstat(pathToFile + sigExtension); !os.IsNotExist(err) {
		_ = os.Remove(pathToFile + sigExtension)
	}
	cmd := exec.Command("gpg", "--local-user", keyID, "--detach-sign", pathToFile) //nolint[gosec]
	return cmd.Run()
}

func verifyFile(pathToFile string) error {
	fileReader, err := os.Open(pathToFile) //nolint[gosec]
	if err != nil {
		return err
	}
	defer fileReader.Close() //nolint[errcheck]

	signatureReader, err := os.Open(pathToFile + sigExtension) //nolint[gosec]
	if err != nil {
		return err
	}
	defer signatureReader.Close() //nolint[errcheck]

	return verifyBytes(fileReader, signatureReader)
}

func verifyBytes(fileReader, signatureReader io.Reader) (err error) {
	if _, err = getPubKey(); err != nil {
		return err
	}

	_, err = openpgp.CheckDetachedSignature(pubkeyRing, fileReader, signatureReader, nil)
	/*
		if err != nil {
			return err
		}

		if signer == nil || signer.PrimaryKey.KeyId != keyID {
			return errors.New("Signer with wrong key ID")
		}
	*/
	return
}

// from opengpg/read_test.go
func getPubKey() (el openpgp.EntityList, err error) {
	if pubkeyRing != nil && len(pubkeyRing) != 0 {
		return pubkeyRing, nil
	}
	data, err := hex.DecodeString(pubkeyHex)
	if err != nil {
		return
	}
	pubkeyRing, err = openpgp.ReadKeyRing(bytes.NewBuffer(data))
	return pubkeyRing, err
}
