package main

import (
	"bookhoard/internal/handlers"
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"testing"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestBulkResolve_MostRecentStrategy(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflict1 := createTestConflict(t, env, makeConflictData(0.45, 0.42))
	conflict2 := createTestConflict(t, env, makeConflictData(0.60, 0.55))

	id1 := uuid.UUID(conflict1.ID.Bytes).String()
	id2 := uuid.UUID(conflict2.ID.Bytes).String()

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{id1, id2},
		Strategy:    "most_recent",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 2, result.Total)
	assert.Equal(t, 2, result.Success)
	assert.Equal(t, 0, result.Failed)
	require.Len(t, result.Results, 2)

	for _, r := range result.Results {
		assert.Equal(t, "success", r.Status)
		assert.Equal(t, "koreader", r.Winner)
	}
}

func TestBulkResolve_HighestProgressStrategy(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflictDataHighKobo := makeConflictData(0.30, 0.90)

	conflict := createTestConflict(t, env, conflictDataHighKobo)
	conflictID := uuid.UUID(conflict.ID.Bytes).String()

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{conflictID},
		Strategy:    "highest_progress",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 1, result.Total)
	assert.Equal(t, 1, result.Success)
	assert.Equal(t, 0, result.Failed)
	assert.Equal(t, "kobo", result.Results[0].Winner)
}

func TestBulkResolve_ManualStrategy(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflict := createTestConflict(t, env, makeConflictData(0.45, 0.42))
	conflictID := uuid.UUID(conflict.ID.Bytes).String()

	req := handlers.BulkResolveRequest{
		ConflictIDs:   []string{conflictID},
		Strategy:      "manual",
		WinningSource: "koreader",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 1, result.Total)
	assert.Equal(t, 1, result.Success)
	assert.Equal(t, "koreader", result.Results[0].Winner)
}

func TestBulkResolve_ManualStrategy_WithoutWinner(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflict := createTestConflict(t, env, makeConflictData(0.45, 0.42))
	conflictID := uuid.UUID(conflict.ID.Bytes).String()

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{conflictID},
		Strategy:    "manual",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 1, result.Failed)
	assert.Equal(t, "error", result.Results[0].Status)
	assert.Contains(t, result.Results[0].Error, "winning_source")
}

func TestBulkResolve_ManualStrategy_InvalidWinner(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflict := createTestConflict(t, env, makeConflictData(0.45, 0.42))
	conflictID := uuid.UUID(conflict.ID.Bytes).String()

	req := handlers.BulkResolveRequest{
		ConflictIDs:   []string{conflictID},
		Strategy:      "manual",
		WinningSource: "nonexistent_device",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 1, result.Failed)
	assert.Contains(t, result.Results[0].Error, "invalid winning source")
}

func TestBulkResolve_ConflictNotFound(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{uuid.New().String()},
		Strategy:    "most_recent",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 1, result.Total)
	assert.Equal(t, 0, result.Success)
	assert.Equal(t, 1, result.Failed)
	assert.Contains(t, result.Results[0].Error, "conflict not found")
}

func TestBulkResolve_EmptyConflictIDs(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{},
		Strategy:    "most_recent",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}

func TestBulkResolve_InvalidConflictID(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{"not-a-uuid"},
		Strategy:    "most_recent",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 1, result.Failed)
	assert.Contains(t, result.Results[0].Error, "invalid conflict ID")
}

func TestBulkResolve_InvalidRequestBody(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer([]byte("invalid json")))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}

func TestBulkResolve_RequiresAuth(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{uuid.New().String()},
		Strategy:    "most_recent",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
}

func TestBulkDismiss_RealConflicts(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflict1 := createTestConflict(t, env, makeConflictData(0.45, 0.42))
	conflict2 := createTestConflict(t, env, makeConflictData(0.60, 0.55))

	id1 := uuid.UUID(conflict1.ID.Bytes).String()
	id2 := uuid.UUID(conflict2.ID.Bytes).String()

	req := map[string]interface{}{
		"conflict_ids": []string{id1, id2},
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, float64(2), result["total"])
	assert.Equal(t, float64(2), result["success"])
	assert.Equal(t, float64(0), result["failed"])

	results := result["results"].([]interface{})
	require.Len(t, results, 2)
	for _, r := range results {
		entry := r.(map[string]interface{})
		assert.Equal(t, "success", entry["status"])
	}
}

func TestBulkDismiss_NotFoundConflict(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := map[string]interface{}{
		"conflict_ids": []string{uuid.New().String()},
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, float64(1), result["total"])
	assert.Equal(t, float64(0), result["success"])
	assert.Equal(t, float64(1), result["failed"])
}

func TestBulkDismiss_InvalidConflictID(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := map[string]interface{}{
		"conflict_ids": []string{"invalid-uuid"},
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, float64(1), result["total"])
	assert.Equal(t, float64(1), result["failed"])
	assert.Contains(t, result["results"].([]interface{})[0].(map[string]interface{})["error"], "invalid conflict ID")
}

func TestBulkDismiss_EmptyConflictIDs(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := map[string]interface{}{
		"conflict_ids": []string{},
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}

func TestBulkDismiss_InvalidRequestBody(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer([]byte("invalid json")))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}

func TestBulkDismiss_RequiresAuth(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	req := map[string]interface{}{
		"conflict_ids": []string{uuid.New().String()},
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/conflicts/bulk-dismiss", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
}

func TestBulkResolve_MixedSuccessAndFailure(t *testing.T) {
	env := setupConflictTest(t)
	client := &http.Client{}

	conflict := createTestConflict(t, env, makeConflictData(0.45, 0.42))
	realID := uuid.UUID(conflict.ID.Bytes).String()

	req := handlers.BulkResolveRequest{
		ConflictIDs: []string{realID, uuid.New().String()},
		Strategy:    "most_recent",
	}
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", env.setup.Server.URL+"/api/conflicts/bulk-resolve", bytes.NewBuffer(body))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+env.setup.Token)

	resp, err := client.Do(httpReq)
	require.NoError(t, err)
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)

	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var result handlers.BulkResolveResponse
	err = json.NewDecoder(resp.Body).Decode(&result)
	require.NoError(t, err)

	assert.Equal(t, 2, result.Total)
	assert.Equal(t, 1, result.Success)
	assert.Equal(t, 1, result.Failed)
}
