// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ipn

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"tailscale.com/atomicfile"
	"tailscale.com/control/controlclient"
)

type Prefs struct {
	RouteAll         bool
	AllowSingleHosts bool
	CorpDNS          bool
	WantRunning      bool
	NotepadURLs      bool
	UsePacketFilter  bool

	// The Persist field is named 'Config' in the file for backward
	// compatibility with earlier versions.
	// TODO(apenwarr): We should move this out of here, it's not a pref.
	//  We can maybe do that once we're sure which module should persist
	//  it (backend or frontend?)
	Persist *controlclient.Persist `json:"Config"`
}

func (uc *Prefs) Pretty() string {
	var ucp string
	if uc.Persist != nil {
		ucp = uc.Persist.Pretty()
	} else {
		ucp = "Persist=nil"
	}
	return fmt.Sprintf("Prefs{ra=%v mesh=%v dns=%v want=%v notepad=%v %v}",
		uc.RouteAll, uc.AllowSingleHosts, uc.CorpDNS, uc.WantRunning,
		uc.NotepadURLs, ucp)
}

func (uc *Prefs) ToBytes() []byte {
	data, err := json.MarshalIndent(uc, "", "\t")
	if err != nil {
		log.Fatalf("Prefs marshal: %v\n", err)
	}
	return data
}

func (uc *Prefs) Equals(uc2 *Prefs) bool {
	b1 := uc.ToBytes()
	b2 := uc2.ToBytes()
	return bytes.Equal(b1, b2)
}

func NewPrefs() Prefs {
	return Prefs{
		// Provide default values for options which are normally
		// true, but might be missing from the json data for any
		// reason. The json can still override them to false.
		RouteAll:         true,
		AllowSingleHosts: true,
		CorpDNS:          true,
		WantRunning:      true,
		UsePacketFilter:  true,
	}
}

func PrefsFromBytes(b []byte, enforceDefaults bool) (Prefs, error) {
	uc := NewPrefs()
	if len(b) == 0 {
		return uc, nil
	}
	persist := &controlclient.Persist{}
	err := json.Unmarshal(b, persist)
	if err == nil && (persist.Provider != "" || persist.LoginName != "") {
		// old-style relaynode config; import it
		uc.Persist = persist
	} else {
		err = json.Unmarshal(b, &uc)
		if err != nil {
			log.Printf("Prefs parse: %v: %v\n", err, b)
		}
	}
	if enforceDefaults {
		uc.RouteAll = true
		uc.AllowSingleHosts = true
	}
	return uc, err
}

func (uc *Prefs) Copy() *Prefs {
	uc2, err := PrefsFromBytes(uc.ToBytes(), false)
	if err != nil {
		log.Fatalf("Prefs was uncopyable: %v\n", err)
	}
	return &uc2
}

func LoadPrefs(filename string, enforceDefaults bool) Prefs {
	log.Printf("Loading prefs %v\n", filename)
	data, err := ioutil.ReadFile(filename)
	uc := NewPrefs()
	if err != nil {
		log.Printf("Read: %v: %v\n", filename, err)
		goto fail
	}
	uc, err = PrefsFromBytes(data, enforceDefaults)
	if err != nil {
		log.Printf("Parse: %v: %v\n", filename, err)
		goto fail
	}
	goto post
fail:
	log.Printf("failed to load config. Generating a new one.\n")
	uc = NewPrefs()
	uc.WantRunning = true
post:
	// Update: we changed our minds :)
	// Versabank would like to persist the setting across reboots, for now,
	// because they don't fully trust the system and want to be able to
	// leave it turned off when not in use. Eventually we need to make
	// all motivation for this go away.
	if false {
		// Usability note: we always want WantRunning = true on startup.
		// That way, if someone accidentally disables their VPN and doesn't
		// know how, rebooting will fix it.
		// We still persist WantRunning just in case we change our minds on
		// this topic.
		uc.WantRunning = true
	}
	log.Printf("Loaded prefs %v %v\n", filename, uc.Pretty())
	return uc
}

func SavePrefs(filename string, uc *Prefs) {
	log.Printf("Saving prefs %v %v\n", filename, uc.Pretty())
	data := uc.ToBytes()
	os.MkdirAll(filepath.Dir(filename), 0700)
	if err := atomicfile.WriteFile(filename, data, 0666); err != nil {
		log.Printf("SavePrefs: %v\n", err)
	}
}