// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

//go:build integration
// +build integration

package integration

import (
	"io"
	"log"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/syncthing/syncthing/lib/config"
	"github.com/syncthing/syncthing/lib/events"
	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/rc"
)

func TestOverride(t *testing.T) {
	// Enable "send-only" on s1/default
	id, _ := protocol.DeviceIDFromString(id1)
	cfg, _, _ := config.Load("h1/config.xml", id, events.NoopLogger)
	fld := cfg.Folders()["default"]
	fld.Type = config.FolderTypeSendOnly
	cfg.SetFolder(fld)
	os.Rename("h1/config.xml", "h1/config.xml.orig")
	defer os.Rename("h1/config.xml.orig", "h1/config.xml")
	cfg.Save()

	log.Println("Cleaning...")
	err := removeAll("s1", "s2", "h1/index*", "h2/index*")
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Generating files...")
	err = generateFiles("s1", 100, 20, "../LICENSE")
	if err != nil {
		t.Fatal(err)
	}

	fd, err := os.Create("s1/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("hello\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	expected, err := directoryContents("s1")
	if err != nil {
		t.Fatal(err)
	}

	sendOnly := startInstance(t, 1)
	defer checkedStop(t, sendOnly)

	sendRecv := startInstance(t, 2)
	defer checkedStop(t, sendRecv)

	sendOnly.ResumeAll()
	sendRecv.ResumeAll()

	log.Println("Syncing...")

	rc.AwaitSync("default", sendOnly, sendRecv)

	log.Println("Verifying...")

	actual, err := directoryContents("s2")
	if err != nil {
		t.Fatal(err)
	}
	err = compareDirectoryContents(actual, expected)
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Changing file on sendRecv side...")

	fd, err = os.OpenFile("s2/testfile.txt", os.O_WRONLY|os.O_APPEND, 0o644)
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("text added to s2\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	if err := sendRecv.Rescan("default"); err != nil {
		t.Fatal(err)
	}

	log.Println("Waiting for index to send...")

	time.Sleep(10 * time.Second)

	log.Println("Hitting Override on sendOnly...")

	if _, err := sendOnly.Post("/rest/db/override?folder=default", nil); err != nil {
		t.Fatal(err)
	}

	log.Println("Syncing...")

	rc.AwaitSync("default", sendOnly, sendRecv)

	// Verify that the override worked

	fd, err = os.Open("s1/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err := io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()

	if strings.Contains(string(bs), "added to s2") {
		t.Error("Change should not have been synced to sendOnly")
	}

	fd, err = os.Open("s2/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err = io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()

	if strings.Contains(string(bs), "added to s2") {
		t.Error("Change should have been overridden on sendRecv")
	}
}

/* This doesn't currently work with detection completion, as we don't actually
get to completion when in sendOnly/sendRecv mode. Needs fixing.

func TestOverrideIgnores(t *testing.T) {
	// Enable "sendOnly" on s1/default
	id, _ := protocol.DeviceIDFromString(id1)
	cfg, _, _ := config.Load("h1/config.xml", id, events.NoopLogger)
	fld := cfg.Folders()["default"]
	fld.ReadOnly = true
	cfg.SetFolder(fld)
	os.Rename("h1/config.xml", "h1/config.xml.orig")
	defer os.Rename("h1/config.xml.orig", "h1/config.xml")
	cfg.Save()

	log.Println("Cleaning...")
	err := removeAll("s1", "s2", "h1/index*", "h2/index*")
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Generating files...")
	err = generateFiles("s1", 10, 2, "../LICENSE")
	if err != nil {
		t.Fatal(err)
	}

	fd, err := os.Create("s1/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("original text\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	expected, err := directoryContents("s1")
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Starting sendOnly...")
	sendOnly := syncthingProcess{ // id1
		instance: "1",
		argv:     []string{"--home", "h1"},
		port:     8081,
		apiKey:   apiKey,
	}
	err = sendOnly.start()
	if err != nil {
		t.Fatal(err)
	}
	defer sendOnly.stop()

	log.Println("Starting sendRecv...")
	sendRecv := syncthingProcess{ // id2
		instance: "2",
		argv:     []string{"--home", "h2"},
		port:     8082,
		apiKey:   apiKey,
	}
	err = sendRecv.start()
	if err != nil {
		sendOnly.stop()
		t.Fatal(err)
	}
	defer sendRecv.stop()

	log.Println("Syncing...")

	err = awaitCompletion("default", sendOnly, sendRecv)
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Verifying...")

	actual, err := directoryContents("s2")
	if err != nil {
		t.Fatal(err)
	}
	err = compareDirectoryContents(actual, expected)
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Ignoring testfile.txt on sendOnly...")

	fd, err = os.Create("s1/.stignore")
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("testfile.txt\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Modify testfile.txt on sendOnly...")

	fd, err = os.Create("s1/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("updated on sendOnly but ignored\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	fd, err = os.Create("s1/testfile2.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("sync me\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	err = sendOnly.rescan("default")

	log.Println("Waiting for sync...")
	time.Sleep(10 * time.Second)

	// Verify that sync worked

	fd, err = os.Open("s2/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err := io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()

	if !strings.Contains(string(bs), "original text") {
		t.Error("Changes should not have been synced to sendRecv")
	}

	fd, err = os.Open("s2/testfile2.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err = io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()

	if !strings.Contains(string(bs), "sync me") {
		t.Error("Changes should have been synced to sendRecv")
	}

	log.Println("Removing file on sendRecv side...")

	os.Remove("s2/testfile.txt")

	err = sendRecv.rescan("default")
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Waiting for sync...")
	time.Sleep(10 * time.Second)

	// Verify that nothing changed

	fd, err = os.Open("s1/testfile.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err = io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()

	if !strings.Contains(string(bs), "updated on sendOnly but ignored") {
		t.Error("Changes should not have been synced to sendOnly")
	}

	fd, err = os.Open("s2/testfile.txt")
	if err == nil {
		t.Error("File should not exist on the sendRecv")
	}

	log.Println("Creating file on sendRecv...")

	fd, err = os.Create("s2/testfile3.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, err = fd.WriteString("created on sendRecv, should be removed on override\n")
	if err != nil {
		t.Fatal(err)
	}
	err = fd.Close()
	if err != nil {
		t.Fatal(err)
	}

	log.Println("Hitting Override on sendOnly...")

	resp, err := sendOnly.post("/rest/db/override?folder=default", nil)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != 200 {
		t.Fatal(resp.Status)
	}

	log.Println("Waiting for sync...")
	time.Sleep(10 * time.Second)

	fd, err = os.Open("s2/testfile.txt")
	if err == nil {
		t.Error("File should not exist on the sendRecv")
	}
	fd, err = os.Open("s2/testfile2.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err = io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()
	if !strings.Contains(string(bs), "sync me") {
		t.Error("Changes should have been synced to sendRecv")
	}
	fd, err = os.Open("s2/testfile3.txt")
	if err != nil {
		t.Error("File should still exist on the sendRecv")
	}
	fd.Close()

	log.Println("Hitting Override on sendOnly (again)...")

	resp, err = sendOnly.post("/rest/db/override?folder=default", nil)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != 200 {
		t.Fatal(resp.Status)
	}

	log.Println("Waiting for sync...")
	time.Sleep(10 * time.Second)

	fd, err = os.Open("s2/testfile.txt")
	if err == nil {
		t.Error("File should not exist on the sendRecv")
	}
	fd, err = os.Open("s2/testfile2.txt")
	if err != nil {
		t.Fatal(err)
	}
	bs, err = io.ReadAll(fd)
	if err != nil {
		t.Fatal(err)
	}
	fd.Close()
	if !strings.Contains(string(bs), "sync me") {
		t.Error("Changes should have been synced to sendRecv")
	}
	fd, err = os.Open("s2/testfile3.txt")
	if err != nil {
		t.Error("File should still exist on the sendRecv")
	}
	fd.Close()

}
*/
