// This file is part of taldir, the Taler Directory implementation.
// Copyright (C) 2022 Martin Schanzenbach
//
// Taldir is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// Taldir 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
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL3.0-or-later

package main

import (
	"crypto/sha512"
	"encoding/binary"
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"path/filepath"

	"gopkg.in/ini.v1"
	"rsc.io/getopt"
	"taler.net/taldir/internal/util"
)

var (
	version string
)

// Hashes the alias with its type in a prefix-free fashion
// SHA512(len(atype||alias)||atype||alias)
func hashAlias(atype string, alias string) []byte {
	h := sha512.New()
	b := make([]byte, 4)
	binary.BigEndian.PutUint32(b, uint32(len(atype)+len(alias)))
	h.Write(b)
	h.Write([]byte(atype))
	h.Write([]byte(alias))
	return h.Sum(nil)
}

// Generates a link from a challenge and alias
func generateLink(host string, alias string, atype string, challenge string) string {
	hAlias := hashAlias(atype, alias)
	hAliasEnc := util.Base32CrockfordEncode(hAlias)
	return host + "/register/" + url.QueryEscape(hAliasEnc) + "/" + url.QueryEscape(challenge) + "?alias=" + url.QueryEscape(alias)
}

func main() {
	var solveFlag = flag.Bool("s", false, "Provide a solution for the challenge/pubkey")
	getopt.Alias("s", "solve")
	var linkFlag = flag.Bool("l", false, "Provide a link for activation")
	getopt.Alias("l", "link")
	var challengeFlag = flag.String("c", "", "Activation challenge")
	getopt.Alias("c", "challenge")
	var pubkeyFlag = flag.String("p", "", "Public key")
	getopt.Alias("p", "public-key")
	var aliasFlag = flag.String("a", "", "Alias")
	getopt.Alias("a", "alias")
	var atypeFlag = flag.String("t", "", "Alias type")
	getopt.Alias("t", "alias-type")
	var versionFlag = flag.Bool("v", false, "Print version")
	getopt.Alias("v", "version")
	flag.Parse()
	cfgfile := "taldir.conf"
	_cfg, err := ini.LooseLoad(cfgfile)
	if err != nil {
		log.Fatalf("Failed to read config: %v", err)
		os.Exit(1)
	}
	if *versionFlag {
		fullName, err := os.Executable()
		if err != nil {
			log.Panic(err)
		}
		fmt.Printf("%s %s", filepath.Base(fullName), version)
		return
	}
	host := _cfg.Section("directory").Key("base_url").MustString("http://localhost")
	if *solveFlag {
		if len(*challengeFlag) == 0 || len(*pubkeyFlag) == 0 {
			fmt.Println("You need to provide an activation challenge and a public key to generate a solution")
			os.Exit(1)
		}
		fmt.Println(util.GenerateSolution(*pubkeyFlag, *challengeFlag))
		os.Exit(0)
	}
	if *linkFlag {
		if len(*challengeFlag) == 0 || len(*aliasFlag) == 0 || len(*atypeFlag) == 0 {
			fmt.Println("You need to provide an activation challenge and an alias to generate a link")
			os.Exit(1)
		}
		fmt.Println(generateLink(host, *aliasFlag, *atypeFlag, *challengeFlag))
		os.Exit(0)
	}
}
