package photos

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestGetAssetsSupportsWrappedItemsResponse(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/assets" {
			http.NotFound(w, r)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]any{
			"items": []map[string]any{
				{
					"id":               "a1",
					"originalFileName": "a.jpg",
					"fileCreatedAt":    "2026-01-01T00:00:00Z",
					"exifInfo": map[string]any{
						"fileSizeInByte": int64(42),
					},
				},
			},
		})
	}))
	defer server.Close()

	client := NewClient(server.URL)
	assets, err := client.GetAssets(context.Background(), "key", 1, 50)
	if err != nil {
		t.Fatalf("GetAssets: %v", err)
	}
	if len(assets) != 1 || assets[0].FileSize != 42 {
		t.Fatalf("assets = %+v", assets)
	}
}

func TestGetAllAssetsPaginatesUpstream(t *testing.T) {
	call := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		call++
		page := r.URL.Query().Get("page")
		if page == "1" {
			batch := make([]Asset, assetsPageSize)
			for i := range batch {
				batch[i] = Asset{ID: fmt.Sprintf("full-%d", i)}
			}
			_ = json.NewEncoder(w).Encode(batch)
			return
		}
		if page == "2" {
			_ = json.NewEncoder(w).Encode([]Asset{
				{ID: "tail-1"},
				{ID: "tail-2"},
			})
			return
		}
		_ = json.NewEncoder(w).Encode([]Asset{})
	}))
	defer server.Close()

	client := NewClient(server.URL)
	assets, err := client.GetAllAssets(context.Background(), "key")
	if err != nil {
		t.Fatalf("GetAllAssets: %v", err)
	}
	if len(assets) != assetsPageSize+2 {
		t.Fatalf("len = %d, want %d", len(assets), assetsPageSize+2)
	}
	if call != 2 {
		t.Fatalf("upstream calls = %d, want 2", call)
	}
}

func TestCreateAlbumMapsImmichPayload(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost || r.URL.Path != "/albums" {
			http.NotFound(w, r)
			return
		}
		if r.Header.Get("x-api-key") != "key" {
			t.Fatalf("api key = %q", r.Header.Get("x-api-key"))
		}
		var payload createAlbumPayload
		if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
			t.Fatalf("decode payload: %v", err)
		}
		if payload.AlbumName != "Trip" || payload.Description != "Summer" || len(payload.AssetIDs) != 1 || payload.AssetIDs[0] != "asset-1" {
			t.Fatalf("payload = %+v", payload)
		}
		w.WriteHeader(http.StatusCreated)
		_ = json.NewEncoder(w).Encode(Album{ID: "album-1", Name: payload.AlbumName})
	}))
	defer server.Close()

	client := NewClient(server.URL)
	album, err := client.CreateAlbum(context.Background(), "key", "Trip", "Summer", []string{"asset-1"})
	if err != nil {
		t.Fatalf("CreateAlbum: %v", err)
	}
	if album.ID != "album-1" || album.Name != "Trip" {
		t.Fatalf("album = %+v", album)
	}
}

func TestUploadAssetStatusMapped(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusBadRequest)
	}))
	defer server.Close()

	client := NewClient(server.URL)
	_, err := client.UploadAsset(context.Background(), "key", strings.NewReader("body"), "application/octet-stream")
	if err == nil {
		t.Fatal("expected error")
	}
	var statusErr *HTTPStatusError
	if !errors.As(err, &statusErr) || statusErr.StatusCode != http.StatusBadRequest {
		t.Fatalf("err = %v", err)
	}
}

func TestGetAlbumNotFound(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.NotFound(w, r)
	}))
	defer server.Close()

	client := NewClient(server.URL)
	_, err := client.GetAlbum(context.Background(), "key", "missing")
	if err == nil {
		t.Fatal("expected error")
	}
	var statusErr *HTTPStatusError
	if !errors.As(err, &statusErr) || statusErr.StatusCode != http.StatusNotFound {
		t.Fatalf("err = %v", err)
	}
}

func TestAddAlbumAssetsSendsBulkIDs(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPut || r.URL.Path != "/albums/album-1/assets" {
			http.NotFound(w, r)
			return
		}
		var payload bulkIDsPayload
		if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
			t.Fatalf("decode payload: %v", err)
		}
		if len(payload.IDs) != 2 || payload.IDs[0] != "a1" || payload.IDs[1] != "a2" {
			t.Fatalf("payload = %+v", payload)
		}
		_ = json.NewEncoder(w).Encode([]BulkAssetResult{{ID: "a1", Success: true}, {ID: "a2", Success: true}})
	}))
	defer server.Close()

	client := NewClient(server.URL)
	results, err := client.AddAlbumAssets(context.Background(), "key", "album-1", []string{"a1", "a2"})
	if err != nil {
		t.Fatalf("AddAlbumAssets: %v", err)
	}
	if len(results) != 2 {
		t.Fatalf("results = %+v", results)
	}
}

func TestRemoveAlbumAssetsUsesDeleteWithBody(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodDelete || r.URL.Path != "/albums/album-1/assets" {
			http.NotFound(w, r)
			return
		}
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("read body: %v", err)
		}
		if string(body) != `{"ids":["a1"]}` {
			t.Fatalf("body = %s", string(body))
		}
		_ = json.NewEncoder(w).Encode([]BulkAssetResult{{ID: "a1", Success: true}})
	}))
	defer server.Close()

	client := NewClient(server.URL)
	results, err := client.RemoveAlbumAssets(context.Background(), "key", "album-1", []string{"a1"})
	if err != nil {
		t.Fatalf("RemoveAlbumAssets: %v", err)
	}
	if len(results) != 1 || !results[0].Success {
		t.Fatalf("results = %+v", results)
	}
}
