package main

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

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

func TestKoboInitialization(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	setup := setupTestServer(t)

	_ = createTestMediaItemID(t, setup)

	log.Printf("[DEBUG] Kobo test setup: creating device and media")

	t.Run("successful initialization", func(t *testing.T) {
		// Create Kobo device using TestDeviceSetup for proper authentication
		deviceSetup := setupDeviceTest(t)
		koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-test")

		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/kobo/"+koboDevice.AuthToken+"/v1/initialization", nil)
		req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
			koboDevice.ID.String(), koboDevice.Identifier))

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

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

func TestKoboLibrarySync(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	setup := setupTestServer(t)

	_ = createTestMediaItemID(t, setup)

	log.Printf("[DEBUG] Kobo test setup: creating device and media")

	t.Run("successful library sync", func(t *testing.T) {
		deviceSetup := setupDeviceTest(t)
		koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-test")

		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/kobo/"+koboDevice.AuthToken+"/v1/initialization", nil)
		req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
			koboDevice.ID.String(), koboDevice.Identifier))

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

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

func TestKoboMarkupSync(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	setup := setupTestServer(t)

	mediaItemID := createTestMediaItemID(t, setup)

	log.Printf("[DEBUG] Kobo test setup: creating device and media")

	t.Run("successful markup sync with annotations and bookmarks", func(t *testing.T) {
		deviceSetup := setupDeviceTest(t)
		koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-markup")

		reqBody := map[string]interface{}{
			"ReadingSync": []map[string]interface{}{
				{
					"ContentId":            mediaItemID,
					"PercentRead":          45.6,
					"EntitlementId":        "ent-123",
					"RemainingTimeMinutes": 120,
					"LastModified":         "2026-01-30T20:00:00Z",
				},
			},
			"BookmarkSync": []map[string]interface{}{
				{
					"BookmarkId":    "bookmark-1",
					"ContentId":     mediaItemID,
					"BookmarkText":  "This is highlighted text",
					"BookmarkType":  "annotation",
					"BookmarkTitle": "Chapter 3",
				},
				{
					"BookmarkId":   "bookmark-2",
					"ContentId":    mediaItemID,
					"BookmarkText": "This is my note about book",
					"BookmarkType": "bookmark",
				},
				{
					"BookmarkId":   "epubcfi(/6/4[chap1]!/4/2/1:0)",
					"ContentId":    mediaItemID,
					"BookmarkType": "last-read-place",
					"Hidden":       true,
					"Chapter":      1,
				},
			},
		}

		body, _ := json.Marshal(reqBody)
		req, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/kobo/"+koboDevice.AuthToken+"/markup", bytes.NewReader(body))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
			koboDevice.ID.String(), koboDevice.Identifier))

		client := &http.Client{}
		resp, err := client.Do(req)
		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.Contains(t, result, "Status")
	})
}

func TestKoboBookmarkSync(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	setup := setupTestServer(t)

	mediaItemID := createTestMediaItemID(t, setup)

	log.Printf("[DEBUG] Kobo test setup: creating device and media")

	t.Run("successful bookmark sync with last-read-place", func(t *testing.T) {
		deviceSetup := setupDeviceTest(t)
		koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-bookmark")

		reqBody := map[string]interface{}{
			"BookmarkSync": []map[string]interface{}{
				{
					"BookmarkId":   "bookmark-3",
					"ContentId":    mediaItemID,
					"BookmarkText": "Important note about book",
					"BookmarkType": "bookmark",
					"DateCreated":  "2026-01-30T19:55:00Z",
				},
				{
					"BookmarkId":   "epubcfi(/6/4[chap1]!/4/2/1:156)",
					"ContentId":    mediaItemID,
					"BookmarkType": "last-read-place",
					"Hidden":       true,
					"Chapter":      1,
					"DateCreated":  "2026-01-30T19:55:00Z",
				},
			},
		}

		body, _ := json.Marshal(reqBody)
		req, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/kobo/"+koboDevice.AuthToken+"/bookmark", bytes.NewReader(body))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
			koboDevice.ID.String(), koboDevice.Identifier))

		client := &http.Client{}
		resp, err := client.Do(req)
		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.Contains(t, result, "Status")
	})
}

func TestKoboAnalyticsGettests(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	setup := setupTestServer(t)

	mediaItemID := createTestMediaItemID(t, setup)

	log.Printf("[DEBUG] Kobo test setup: creating device and media")

	t.Run("successful analytics tests", func(t *testing.T) {
		deviceSetup := setupDeviceTest(t)
		koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-analytics")

		reqBody := []map[string]interface{}{
			{
				"ContentId":        mediaItemID,
				"ReadingEvent":     "Reading",
				"RemainingTimeMin": 180,
				"PercentRead":      67.8,
			},
		}

		body, _ := json.Marshal(reqBody)
		req, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/kobo/"+koboDevice.AuthToken+"/v1/analytics/gettests", bytes.NewReader(body))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
			koboDevice.ID.String(), koboDevice.Identifier))

		client := &http.Client{}
		resp, err := client.Do(req)
		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.Contains(t, result, "Status")
	})
}

func TestKoboDeviceHeaderParsing(t *testing.T) {
	t.Run("valid device header", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"DeviceId":     "kobo-clara-test",
			"Model":        "Kobo Clara",
			"SerialNumber": "N123456789",
			"Firmware":     "4.38.23555",
		}

		jsonData, _ := json.Marshal(reqBody)

		var device handlers.KoboDeviceInfo
		err := json.Unmarshal(jsonData, &device)

		require.NoError(t, err)
		assert.Equal(t, "kobo-clara-test", device.DeviceID)
		assert.Equal(t, "Kobo Clara", device.Model)
		assert.Equal(t, "N123456789", device.SerialNumber)
		assert.Equal(t, "4.38.23555", device.Firmware)
	})
}

func closeTestServer(t *testing.T, ts interface{}, db interface{}) {
	if ts, ok := ts.(*httptest.Server); ok {
		ts.Close()
	}
}
