//go:build integration

package migration_test

import (
	"context"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/ultisuite/ulti-backend/internal/integrationtest"
	"github.com/ultisuite/ulti-backend/internal/mail/credentials"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
	"github.com/ultisuite/ulti-backend/internal/users"
)

func mockNextcloudServer(t *testing.T) *httptest.Server {
	t.Helper()
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodPut, "MKCOL", "MKCALENDAR":
			w.WriteHeader(http.StatusCreated)
		case http.MethodDelete:
			w.WriteHeader(http.StatusNoContent)
		case "PROPFIND", "REPORT", http.MethodGet:
			w.WriteHeader(http.StatusOK)
			_, _ = w.Write([]byte(`<?xml version="1.0"?><d:multistatus xmlns:d="DAV:"></d:multistatus>`))
		default:
			w.WriteHeader(http.StatusNoContent)
		}
	}))
}

func testCredentialManager(t *testing.T) *credentials.Manager {
	t.Helper()
	mgr, err := credentials.NewManager(
		"v1:MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=",
		"v1",
	)
	if err != nil {
		t.Fatalf("credential manager: %v", err)
	}
	return mgr
}

func mockNextcloudClient(t *testing.T, pool *pgxpool.Pool, email string) (*nextcloud.Client, string) {
	t.Helper()
	srv := mockNextcloudServer(t)
	t.Cleanup(srv.Close)

	ncUserID := nextcloud.UserIDFromClaims(email, "")
	store := nextcloud.NewDAVCredentialStore(pool, testCredentialManager(t))
	if err := store.SaveToken(context.Background(), ncUserID, "mock-app-password"); err != nil {
		t.Fatalf("seed dav token: %v", err)
	}
	client := nextcloud.NewClient(srv.URL, "admin", "admin").WithDAVCredentials(store)
	return client, ncUserID
}

func insertMigrationTestUser(t *testing.T, pool *pgxpool.Pool, prefix string) (userID, email string) {
	t.Helper()
	claims := integrationtest.RegularUser(integrationtest.NewExternalID(prefix))
	claims.Email = prefix + "-" + uuid.NewString() + "@migration.test"
	id, err := users.EnsureUser(context.Background(), pool, claims)
	integrationtest.FailIf(err, t, "ensure user")
	return id, claims.Email
}

func googleRewriteClient(t *testing.T, handler http.HandlerFunc) *http.Client {
	t.Helper()
	srv := httptest.NewServer(handler)
	t.Cleanup(srv.Close)
	return &http.Client{
		Transport: &hostRewriteTransport{
			mockBase: srv.URL,
			match: func(host string) bool {
				return strings.Contains(host, "googleapis.com")
			},
		},
	}
}

func graphRewriteClient(t *testing.T, handler http.HandlerFunc) *http.Client {
	t.Helper()
	srv := httptest.NewServer(handler)
	t.Cleanup(srv.Close)
	return &http.Client{
		Transport: &hostRewriteTransport{
			mockBase: srv.URL,
			match: func(host string) bool {
				return strings.Contains(host, "graph.microsoft.com")
			},
		},
	}
}

type hostRewriteTransport struct {
	mockBase string
	match    func(host string) bool
}

func (rt *hostRewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if rt.match(req.URL.Host) {
		mockURL, err := url.Parse(rt.mockBase)
		if err != nil {
			return nil, err
		}
		req.URL.Scheme = mockURL.Scheme
		req.URL.Host = mockURL.Host
	}
	return http.DefaultTransport.RoundTrip(req)
}
