package store

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/mjl-/mox/mlog"
	"github.com/mjl-/mox/mox-"
)

func TestExport(t *testing.T) {
	// Set up an account, add 2 messages to different 2 mailboxes. export as tar/zip
	// and maildir/mbox. check there are 2 files in the repo, no errors.txt.

	os.RemoveAll("../testdata/store/data")
	mox.ConfigStaticPath = "../testdata/store/mox.conf"
	mox.MustLoadConfig()
	acc, err := OpenAccount("mjl")
	tcheck(t, err, "open account")
	defer acc.Close()
	switchDone := Switchboard()
	defer close(switchDone)

	log := mlog.New("export")

	msgFile, err := os.CreateTemp("", "mox-test-export")
	tcheck(t, err, "create temp")
	defer os.Remove(msgFile.Name()) // To be sure.
	const msg = "test: test\r\n\r\ntest\r\n"
	_, err = msgFile.Write([]byte(msg))
	tcheck(t, err, "write message")

	m := Message{Received: time.Now(), Size: int64(len(msg))}
	err = acc.DeliverMailbox(xlog, "Inbox", &m, msgFile, false)
	tcheck(t, err, "deliver")

	m = Message{Received: time.Now(), Size: int64(len(msg))}
	err = acc.DeliverMailbox(xlog, "Trash", &m, msgFile, true)
	tcheck(t, err, "deliver")

	var maildirZip, maildirTar, mboxZip, mboxTar bytes.Buffer

	archive := func(archiver Archiver, maildir bool) {
		t.Helper()
		err = ExportMessages(log, acc.DB, acc.Dir, archiver, maildir, "")
		tcheck(t, err, "export messages")
		err = archiver.Close()
		tcheck(t, err, "archiver close")
	}

	os.RemoveAll("../testdata/exportmaildir")
	os.RemoveAll("../testdata/exportmbox")

	archive(ZipArchiver{zip.NewWriter(&maildirZip)}, true)
	archive(ZipArchiver{zip.NewWriter(&mboxZip)}, false)
	archive(TarArchiver{tar.NewWriter(&maildirTar)}, true)
	archive(TarArchiver{tar.NewWriter(&mboxTar)}, false)
	archive(DirArchiver{"../testdata/exportmaildir"}, true)
	archive(DirArchiver{"../testdata/exportmbox"}, false)

	if r, err := zip.NewReader(bytes.NewReader(maildirZip.Bytes()), int64(maildirZip.Len())); err != nil {
		t.Fatalf("reading maildir zip: %v", err)
	} else if len(r.File) != 2*3+2 {
		t.Fatalf("maildir zip, expected 2*3 dirs, and 2 files, got %d files", len(r.File))
	}

	if r, err := zip.NewReader(bytes.NewReader(mboxZip.Bytes()), int64(mboxZip.Len())); err != nil {
		t.Fatalf("reading mbox zip: %v", err)
	} else if len(r.File) != 2 {
		t.Fatalf("maildir zip, 2 files, got %d files", len(r.File))
	}

	checkTarFiles := func(r io.Reader, n int) {
		t.Helper()
		tr := tar.NewReader(r)
		have := 0
		for {
			h, err := tr.Next()
			if err == io.EOF {
				break
			}
			have++
			if h.Name == "errors.txt" {
				t.Fatalf("got errors.txt")
			}
			_, err = io.Copy(io.Discard, tr)
			tcheck(t, err, "copy")
		}
		if have != n {
			t.Fatalf("got %d files, expected %d", have, n)
		}
	}

	checkTarFiles(&maildirTar, 2*3+2)
	checkTarFiles(&mboxTar, 2)

	checkDirFiles := func(dir string, n int) {
		t.Helper()
		have := 0
		err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
			if err == nil && !d.IsDir() {
				have++
			}
			return nil
		})
		tcheck(t, err, "walkdir")
		if n != have {
			t.Fatalf("got %d files, expected %d", have, n)
		}
	}

	checkDirFiles("../testdata/exportmaildir", 2)
	checkDirFiles("../testdata/exportmbox", 2)
}
