// remove_folder_takedown_test.go — arcrun-rag#46：按下「移除」之後，資料真的要被收回。
//
// leo 2026-08-16 實撞：「我去把 Logseq plugin 刪掉以後，**採集的 wiki 沒消失**。」
// 真兇：RemoveFolder 只把路徑從 WatchFolders 拿掉、存檔、重啟看守——**一次都沒碰撤除**。
//
// 本檔釘 App 這一半的三件事：
//  1. 選了「連同雲端一起收回」⇒ 資料夾要進 retiring_folders（collector 靠它才知道要撤）
//  2. 選了「只停止同步」⇒ 行為與從前一字不差（不可以偷偷幫使用者刪東西）
//  3. collector 回報收乾淨了 ⇒ 設定裡那一筆才消失（且中途不會被靜默丟掉）
package main

import (
	"encoding/json"
	"os"
	"testing"

	collector "arcrun-rag/collector"
)

func newTestCfgWithFolder(t *testing.T, path string) {
	t.Helper()
	cfg := &directConfig{
		Accounts: []accountCfg{{
			CypherURL: "https://example.workers.dev", Namespace: "abc123", APIKey: "abc123",
			WatchFolders: []string{path, "/other/folder"},
		}},
		Extractor: "workers-ai",
	}
	if err := saveCfg(cfg); err != nil {
		t.Fatalf("存檔失敗：%v", err)
	}
}

// ① 選「連同雲端一起收回」：離開看守清單、進入待撤清單。
func TestRemoveFolderWithTakedownQueuesRetirement(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)
	t.Setenv("USERPROFILE", home)
	newTestCfgWithFolder(t, "/kb/logseq-plugin")

	if err := (&App{}).RemoveFolder(0, "/kb/logseq-plugin", true, false); err != nil {
		t.Fatalf("移除失敗：%v", err)
	}
	cfg, err := loadCfg()
	if err != nil {
		t.Fatal(err)
	}
	for _, f := range cfg.Accounts[0].WatchFolders {
		if f == "/kb/logseq-plugin" {
			t.Error("移除後不該還在看守清單裡")
		}
	}
	if len(cfg.Accounts[0].RetiringFolders) != 1 || cfg.Accounts[0].RetiringFolders[0] != "/kb/logseq-plugin" {
		t.Fatalf("🔴 這就是 #46 的真兇：移除沒有排任何撤除，retiring_folders=%v",
			cfg.Accounts[0].RetiringFolders)
	}
	// 別的資料夾不能被波及
	if len(cfg.Accounts[0].WatchFolders) != 1 || cfg.Accounts[0].WatchFolders[0] != "/other/folder" {
		t.Errorf("其他資料夾被動到了：%v", cfg.Accounts[0].WatchFolders)
	}

	// 🔴 t108 鏡像檢查：欄位要真的落在磁碟上、且 collector 讀得回來。
	// （兩份 struct 各自維護，少一欄就會在下次存檔靜默消失。）
	raw, _ := os.ReadFile(configPath())
	var m map[string]any
	if err := json.Unmarshal(raw, &m); err != nil {
		t.Fatal(err)
	}
	accs, _ := m["accounts"].([]any)
	if len(accs) == 0 {
		t.Fatal("accounts 不見了")
	}
	a0, _ := accs[0].(map[string]any)
	if _, ok := a0["retiring_folders"]; !ok {
		t.Error("retiring_folders 沒有寫進磁碟 ⇒ collector 永遠不會知道要撤除")
	}
	cc, err := collector.LoadDirectConfig(configPath())
	if err != nil {
		t.Fatalf("collector 讀不了 App 存的 config：%v", err)
	}
	if got := cc.RetiringRoots(); len(got) != 1 || got[0] != "/kb/logseq-plugin" {
		t.Errorf("collector 端看到的待撤清單=%v", got)
	}
}

// ② 選「只停止同步」：行為與從前一字不差——不可以幫使用者做他沒選的刪除。
func TestRemoveFolderWithoutTakedownKeepsCloudData(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)
	t.Setenv("USERPROFILE", home)
	newTestCfgWithFolder(t, "/kb/keep-cloud")

	if err := (&App{}).RemoveFolder(0, "/kb/keep-cloud", false, false); err != nil {
		t.Fatalf("移除失敗：%v", err)
	}
	cfg, _ := loadCfg()
	for _, f := range cfg.Accounts[0].WatchFolders {
		if f == "/kb/keep-cloud" {
			t.Error("移除後不該還在看守清單裡")
		}
	}
	if len(cfg.Accounts[0].RetiringFolders) != 0 {
		t.Errorf("使用者選的是「保留雲端資料」，不該排撤除：%v", cfg.Accounts[0].RetiringFolders)
	}
}

// ③ 收乾淨了才從設定裡消失；還在撤的中途不可以被清掉（清掉＝待辦永久遺失）。
func TestRetirementClearedOnlyWhenCollectorSaysDone(t *testing.T) {
	cfg := &directConfig{Accounts: []accountCfg{{
		RetiringFolders: []string{"/kb/finished", "/kb/still-going", "/kb/never-reported"},
	}}}

	sync := syncStatus{Retiring: map[string]collector.RetiringStatus{
		"/kb/finished":    {Done: true},
		"/kb/still-going": {Remaining: 12, LastError: "HTTP 500：boom"},
		// "/kb/never-reported" 刻意沒被回報（collector 還沒跑到）
	}}

	if !pruneFinishedRetirements(cfg, sync) {
		t.Fatal("有一筆已完成，應回報有變動")
	}
	got := cfg.Accounts[0].RetiringFolders
	if len(got) != 2 || got[0] != "/kb/still-going" || got[1] != "/kb/never-reported" {
		t.Fatalf("只有 done 的那一筆該消失，實得 %v", got)
	}
	// 冪等：再跑一次不該有變動（不然每輪都在寫 config）
	if pruneFinishedRetirements(cfg, sync) {
		t.Error("沒有新的完成項時不該回報變動")
	}
}

// ④ 收回中的資料夾不准同時加回來看守——一邊撤一邊傳，結果不可預測。
func TestAddFolderRefusedWhileRetiring(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)
	t.Setenv("USERPROFILE", home)
	cfg := &directConfig{Accounts: []accountCfg{{
		CypherURL: "https://example.workers.dev", Namespace: "abc123",
		RetiringFolders: []string{"/kb/retiring"},
	}}}
	if err := saveCfg(cfg); err != nil {
		t.Fatal(err)
	}
	if err := (&App{}).AddFolder(0, "/kb/retiring"); err == nil {
		t.Error("正在收回的資料夾應該擋下來並說明原因，不該默默加回去")
	}
	after, _ := loadCfg()
	if len(after.Accounts[0].WatchFolders) != 0 {
		t.Errorf("被擋下就不該寫進看守清單：%v", after.Accounts[0].WatchFolders)
	}
}
