package parser

import (
	"io"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync/atomic"
	"testing"
)

func TestPDFParser_ParseWithResult_MinerUMarkdownIntegration(t *testing.T) {
	withSSRFBypass(t)
	var submitCalled atomic.Bool
	var resultCalled atomic.Bool

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodPost && r.URL.Path == "/file_parse":
			submitCalled.Store(true)
			if got, want := r.Header.Get("Authorization"), "Bearer secret"; got != want {
				t.Errorf("Authorization = %q, want %q", got, want)
				return
			}
			reader, err := r.MultipartReader()
			if err != nil {
				t.Errorf("MultipartReader: %v", err)
				return
			}
			var backend string
			var fileSeen bool
			for {
				part, err := reader.NextPart()
				if err == io.EOF {
					break
				}
				if err != nil {
					t.Errorf("NextPart: %v", err)
					return
				}
				if part.FormName() == "backend" {
					body, _ := io.ReadAll(part)
					backend = string(body)
				}
				if part.FormName() == "files" {
					fileSeen = true
					body, _ := io.ReadAll(part)
					if !strings.HasPrefix(string(body), "%PDF") {
						t.Errorf("uploaded file = %q, want PDF bytes", string(body))
						return
					}
				}
			}
			if backend != "pipeline" {
				t.Errorf("backend = %q, want pipeline", backend)
				return
			}
			if !fileSeen {
				t.Error("multipart upload missing files part")
				return
			}
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"data":{"task_id":"task-1"}}`))
		case r.Method == http.MethodGet && r.URL.Path == "/tasks/task-1/result":
			resultCalled.Store(true)
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"results":{"doc":{"md_content":"# Title\n\nBody paragraph.\n"}}}`))
		default:
			http.NotFound(w, r)
		}
	}))
	defer server.Close()

	pdf := NewPDFParser()
	pdf.ConfigureFromSetup(map[string]any{
		"parse_method":     "MinerU",
		"output_format":    "markdown",
		"mineru_apiserver": server.URL,
		"mineru_api_key":   "secret",
		"mineru_backend":   "pipeline",
	})

	ctx := t.Context()
	res := pdf.ParseWithResult(ctx, "sample.pdf", []byte("%PDF-1.4\nmock"))
	if res.Err != nil {
		t.Fatalf("ParseWithResult: %v", res.Err)
	}
	if !submitCalled.Load() || !resultCalled.Load() {
		t.Fatalf("submit/result called = %v/%v, want true/true", submitCalled.Load(), resultCalled.Load())
	}
	if got, want := res.OutputFormat, "markdown"; got != want {
		t.Fatalf("OutputFormat = %q, want %q", got, want)
	}
	if got, want := res.Markdown, "# Title\n\nBody paragraph.\n"; got != want {
		t.Fatalf("Markdown = %q, want %q", got, want)
	}
	if got, want := res.File["name"], "sample.pdf"; got != want {
		t.Fatalf("File.name = %v, want %v", got, want)
	}
}

func TestPDFParser_ParseWithResult_MinerUJSONIntegration(t *testing.T) {
	withSSRFBypass(t)
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodPost && r.URL.Path == "/file_parse":
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"data":{"task_id":"task-2"}}`))
		case r.Method == http.MethodGet && r.URL.Path == "/tasks/task-2/result":
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"results":{"doc":{"md_content":"# Title\n\nBody paragraph.\n"}}}`))
		default:
			http.NotFound(w, r)
		}
	}))
	defer server.Close()

	pdf := NewPDFParser()
	pdf.ConfigureFromSetup(map[string]any{
		"parse_method":     "MinerU",
		"output_format":    "json",
		"mineru_apiserver": server.URL,
	})

	ctx := t.Context()
	res := pdf.ParseWithResult(ctx, "sample.pdf", []byte("%PDF-1.4\nmock"))
	if res.Err != nil {
		t.Fatalf("ParseWithResult: %v", res.Err)
	}
	if got, want := res.OutputFormat, "json"; got != want {
		t.Fatalf("OutputFormat = %q, want %q", got, want)
	}
	if len(res.JSON) == 0 {
		t.Fatal("JSON is empty; want markdown-normalized items")
	}
	if got := res.JSON[0]["text"]; got == nil {
		t.Fatal("JSON[0].text missing")
	}
}

func TestPDFParser_ParseWithResult_MinerURequiresAPIServer(t *testing.T) {
	pdf := NewPDFParser()
	pdf.ConfigureFromSetup(map[string]any{"parse_method": "MinerU"})

	ctx := t.Context()
	res := pdf.ParseWithResult(ctx, "sample.pdf", []byte("%PDF-1.4\nmock"))
	if res.Err == nil {
		t.Fatal("ParseWithResult: want error when mineru_apiserver is missing, got nil")
	}
	if !strings.Contains(res.Err.Error(), "mineru_apiserver") {
		t.Fatalf("error = %q, want mineru_apiserver context", res.Err.Error())
	}
}

func TestMinerUUploadShape(t *testing.T) {
	body := &strings.Builder{}
	writer := multipart.NewWriter(body)
	_ = writer.WriteField("backend", "pipeline")
	if err := writer.Close(); err != nil {
		t.Fatalf("Close: %v", err)
	}
	if !strings.Contains(body.String(), "pipeline") {
		t.Fatalf("multipart body = %q, want backend field", body.String())
	}
}
