package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"testing"

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

type DashboardIntegrationTestSuite struct {
	suite.Suite
	setup *TestServerSetup
}

func (s *DashboardIntegrationTestSuite) SetupSuite() {
	s.setup = setupTestServer(s.T())
}

func (s *DashboardIntegrationTestSuite) TearDownSuite() {
	s.setup.Close()
}

func (s *DashboardIntegrationTestSuite) TestGetSections_EndToEndFlow() {
	token := s.setup.Token

	// Create test library
	libraryID := createTestLibraryWithFolder(s.T(), s.setup.Server, token, "Test Library", false)

	// Execute API call
	url := fmt.Sprintf("%s/api/dashboard/sections?library_id=%s", s.setup.Server.URL, libraryID)
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+token)

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

	assert.Equal(s.T(), http.StatusOK, resp.StatusCode)

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

	sections, ok := response["sections"].([]interface{})
	require.True(s.T(), ok, "sections should be an array")
	require.Len(s.T(), sections, 5, "Should have 5 system collections")

	// Verify response structure
	sectionMap := make(map[string]map[string]interface{})
	for _, sec := range sections {
		section := sec.(map[string]interface{})
		sectionMap[section["id"].(string)] = section

		// Verify field types
		assert.IsType(s.T(), false, section["is_system"], "is_system should be boolean")
		assert.IsType(s.T(), "", section["title"], "title should be string")
		assert.IsType(s.T(), "", section["description"], "description should be string")
		assert.IsType(s.T(), "", section["icon"], "icon should be string")
		assert.IsType(s.T(), float64(0), section["priority"], "priority should be number")
	}

	// Verify all system collections exist
	assert.Contains(s.T(), sectionMap, "continue-reading")
	assert.Contains(s.T(), sectionMap, "recently-added")
	assert.Contains(s.T(), sectionMap, "recently-read")
	assert.Contains(s.T(), sectionMap, "not-started")
	assert.Contains(s.T(), sectionMap, "continue-series")

	// Verify continue-reading is a system collection
	continueReading := sectionMap["continue-reading"]
	assert.True(s.T(), continueReading["is_system"].(bool), "continue-reading should be system collection")
	assert.Equal(s.T(), "📖", continueReading["icon"], "icon should match")
}

func (s *DashboardIntegrationTestSuite) TestGetSections_MissingLibraryID() {
	token := s.setup.Token

	req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/dashboard/sections", nil)
	req.Header.Set("Authorization", "Bearer "+token)

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

	assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
}

func (s *DashboardIntegrationTestSuite) TestGetSections_InvalidLibraryID() {
	token := s.setup.Token

	req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/dashboard/sections?library_id=invalid-uuid", nil)
	req.Header.Set("Authorization", "Bearer "+token)

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

	assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
}

func (s *DashboardIntegrationTestSuite) TestGetSections_Unauthorized() {
	req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/dashboard/sections?library_id="+uuid.New().String(), nil)

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

	assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
}

func (s *DashboardIntegrationTestSuite) TestUpdatePreferences_Success() {
	token := s.setup.Token
	libraryID := createTestLibraryWithFolder(s.T(), s.setup.Server, token, "Test Library", false)

	reqBody := map[string]interface{}{
		"library_id":         libraryID,
		"hidden_collections": []string{"not-started"},
		"collection_order":   []string{"recently-added", "continue-reading", "recently-read"},
		"items_per_section":  20,
	}
	body, _ := json.Marshal(reqBody)

	req, _ := http.NewRequest("PUT", s.setup.Server.URL+"/api/dashboard/preferences", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)

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

	assert.Equal(s.T(), http.StatusOK, resp.StatusCode)

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

	assert.Contains(s.T(), response, "hidden_collections")
	assert.Contains(s.T(), response, "collection_order")
	assert.Contains(s.T(), response, "items_per_section")
}

func (s *DashboardIntegrationTestSuite) TestUpdatePreferences_Unauthorized() {
	reqBody := map[string]interface{}{
		"library_id":         uuid.New().String(),
		"hidden_collections": []string{},
		"collection_order":   []string{},
		"items_per_section":  20,
	}
	body, _ := json.Marshal(reqBody)

	req, _ := http.NewRequest("PUT", s.setup.Server.URL+"/api/dashboard/preferences", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")

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

	assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
}

func (s *DashboardIntegrationTestSuite) TestRestoreSystemCollection_InvalidName() {
	token := s.setup.Token

	reqBody := map[string]interface{}{
		"collection_name": "invalid-collection-name",
	}
	body, _ := json.Marshal(reqBody)

	req, _ := http.NewRequest("POST", s.setup.Server.URL+"/api/dashboard/restore-system-collection", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)

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

	assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
}

func (s *DashboardIntegrationTestSuite) TestRestoreSystemCollection_Unauthorized() {
	reqBody := map[string]interface{}{
		"collection_name": "continue-reading",
	}
	body, _ := json.Marshal(reqBody)

	req, _ := http.NewRequest("POST", s.setup.Server.URL+"/api/dashboard/restore-system-collection", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")

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

	assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
}

func (s *DashboardIntegrationTestSuite) TestRestoreSystemCollection_ValidNames() {
	token := s.setup.Token

	validCollections := []string{"Continue Reading", "Recently Added", "Recently Read", "Not Started", "Continue Series"}

	for _, collName := range validCollections {
		s.T().Run(collName, func(t *testing.T) {
			reqBody := map[string]interface{}{
				"collection_name": collName,
			}
			body, _ := json.Marshal(reqBody)

			req, _ := http.NewRequest("POST", s.setup.Server.URL+"/api/dashboard/restore-system-collection", bytes.NewBuffer(body))
			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("Authorization", "Bearer "+token)

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

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

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

			assert.Contains(t, response, "message")
		})
	}
}

func TestDashboardIntegrationTestSuite(t *testing.T) {
	suite.Run(t, new(DashboardIntegrationTestSuite))
}
