// Copyright 2022 Martin Dosch.
// Use of this source code is governed by the BSD-2-clause
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"errors"
	"log"
	"os"
	"os/exec"
	"os/user"
	"runtime"
	"strconv"
	"strings"
)

func findConfig() (string, error) {
	// Get the current user.
	curUser, err := user.Current()
	if err != nil {
		return "", err
	}
	// Get home directory.
	home := curUser.HomeDir
	if home == "" {
		return "", errors.New("no home directory found")
	}
	osConfigDir := os.Getenv("$XDG_CONFIG_HOME")
	if osConfigDir == "" {
		osConfigDir = home + "/.config"
	}
	configFiles := [3]string{osConfigDir + "/go-sendxmpp/config",
		osConfigDir + "/go-sendxmpp/sendxmpprc", home +
			"/.sendxmpprc"}

	for _, r := range configFiles {
		// Check that the config file is existing.
		_, err := os.Stat(r)
		if err == nil {
			return r, nil
		}
	}
	return "", errors.New("no configuration file found")
}

// Opens the config file and returns the specified values
// for username, server and port.
func parseConfig(configPath string) (configuration, error) {

	var (
		output configuration
		err    error
	)

	// Use $XDG_CONFIG_HOME/.config/go-sendxmpp/config,
	// $XDG_CONFIG_HOME/.config/go-sendxmpp/sendxmpprc or
	// ~/.sendxmpprc if no config path is specified.
	// Get systems user config path.
	if configPath == "" {
		configPath, err = findConfig()
		if err != nil {
			log.Fatal(err)
		}
	}

	// Only check file permissions if we are not running on windows.
	if runtime.GOOS != "windows" {
		info, err := os.Stat(configPath)
		if err != nil {
			log.Fatal(err)
		}
		// Check for file permissions. Must be 600, 640, 440 or 400.
		perm := info.Mode().Perm()
		permissions := strconv.FormatInt(int64(perm), 8)
		if permissions != "600" && permissions != "640" && permissions != "440" && permissions != "400" {
			return output, errors.New("Wrong permissions for " + configPath + ": " +
				permissions + " instead of 400, 440, 600 or 640.")
		}
	}

	// Open config file.
	file, err := os.Open(configPath)
	if err != nil {
		return output, err
	}
	scanner := bufio.NewScanner(file)
	scanner.Split(bufio.ScanLines)

	// Read config file per line.
	for scanner.Scan() {
		if strings.HasPrefix(scanner.Text(), "#") {
			continue
		}

		row := strings.SplitN(scanner.Text(), " ", 2)

		switch row[0] {
		case "username:":
			output.username = row[1]
		case "jserver:":
			output.jserver = row[1]
		case "password:":
			output.password = row[1]
		case "eval_password:":
			shell := os.Getenv("SHELL")
			if shell == "" {
				shell = "/bin/sh"
			}
			out, err := exec.Command(shell, "-c", row[1]).Output()
			if err != nil {
				log.Fatal(err)
			}
			output.password = string(out)
			if output.password[len(output.password)-1] == '\n' {
				output.password = output.password[:len(output.password)-1]
			}
		case "port:":
			output.port = row[1]
		case "resource:":
			output.resource = row[1]
		default:
			if len(row) >= 2 {
				if strings.Contains(scanner.Text(), ";") {
					output.username = strings.Split(row[0], ";")[0]
					output.jserver = strings.Split(row[0], ";")[1]
					output.password = row[1]
				} else {
					output.username = strings.Split(row[0], ":")[0]
					jserver := strings.Split(row[0], "@")
					if len(jserver) < 2 {
						log.Fatal("Couldn't parse config: ", row[0])
					}
					output.jserver = jserver[0]
					output.password = row[1]
				}
			}
		}
	}
	file.Close()

	// Check if the username is a valid JID
	output.username, err = MarshalJID(output.username)
	if err != nil {
		// Check whether only the local part was used by appending an @ and the
		// server part.
		output.username = output.username + "@" + output.jserver
		// Check if the username is a valid JID now
		output.username, err = MarshalJID(output.username)
		if err != nil {
			return output, errors.New("invalid username/JID: " + output.username)
		}
	}

	return output, err
}
