package services

import (
	"testing"
	"time"

	"mgit.msbls.de/m/paliad/internal/models"
)

func party(name, role string) models.Party {
	return models.Party{Name: name, Role: strPtr(role)}
}

func proceeding(jurisdiction, code string) *models.ProceedingType {
	return &models.ProceedingType{Jurisdiction: strPtr(jurisdiction), Code: code}
}

func projectSide(side string) *models.Project {
	if side == "" {
		return &models.Project{}
	}
	return &models.Project{OurSide: strPtr(side)}
}

// noon UTC on 2026-05-31 → 14:00 Europe/Berlin (CEST), same calendar day.
var fixedNow = time.Date(2026, 5, 31, 12, 0, 0, 0, time.UTC)

func TestAutoSubmissionTitle(t *testing.T) {
	cases := []struct {
		name      string
		clientName string
		project   *models.Project
		parties   []models.Party
		pt        *models.ProceedingType
		want      string
	}{
		{
			name:       "full data — UPC, we are claimant",
			clientName: "Bayer AG",
			project:    projectSide("claimant"),
			parties:    []models.Party{party("Novartis Pharma", "Beklagte")},
			pt:         proceeding("UPC", "upc.inf.cfi"),
			want:       "2026-05-31 Bayer AG ./. UPC ./. Novartis Pharma",
		},
		{
			name:       "full data — German court, we are respondent",
			clientName: "Bayer AG",
			project:    projectSide("respondent"),
			parties:    []models.Party{party("Acme Generics", "Klägerin")},
			pt:         proceeding("DE", "de.null.bpatg"),
			want:       "2026-05-31 Bayer AG ./. BPatG ./. Acme Generics",
		},
		{
			name:       "no opponent — opposing bucket empty",
			clientName: "Bayer AG",
			project:    projectSide("claimant"),
			parties:    []models.Party{party("Bayer AG", "Klägerin")}, // only our own side
			pt:         proceeding("UPC", "upc.inf.cfi"),
			want:       "2026-05-31 Bayer AG ./. UPC",
		},
		{
			name:       "no forum — proceeding type missing",
			clientName: "Bayer AG",
			project:    projectSide("respondent"),
			parties:    []models.Party{party("Acme Generics", "Klägerin")},
			pt:         nil,
			want:       "2026-05-31 Bayer AG ./. Acme Generics",
		},
		{
			name:       "no client — client segment omitted",
			clientName: "",
			project:    projectSide("claimant"),
			parties:    []models.Party{party("Novartis Pharma", "Beklagte")},
			pt:         proceeding("UPC", "upc.inf.cfi"),
			want:       "2026-05-31 UPC ./. Novartis Pharma",
		},
		{
			name:       "all identity segments missing — date only",
			clientName: "",
			project:    projectSide(""), // no our_side → no opponent posture
			parties:    nil,
			pt:         nil,
			want:       "2026-05-31",
		},
		{
			name:       "unknown side — opponent omitted even with parties",
			clientName: "Bayer AG",
			project:    projectSide("third_party"),
			parties:    []models.Party{party("Acme Generics", "Klägerin")},
			pt:         proceeding("EPA", "epa.opp.opd"),
			want:       "2026-05-31 Bayer AG ./. EPA",
		},
		{
			name:       "nil project — opponent omitted, client + forum stand",
			clientName: "Bayer AG",
			project:    nil,
			parties:    []models.Party{party("Acme Generics", "Klägerin")},
			pt:         proceeding("DPMA", "dpma.opp.dpma"),
			want:       "2026-05-31 Bayer AG ./. DPMA",
		},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := AutoSubmissionTitle(fixedNow, c.clientName, c.project, c.parties, c.pt)
			if got != c.want {
				t.Errorf("AutoSubmissionTitle = %q, want %q", got, c.want)
			}
		})
	}
}

// TestAutoSubmissionTitleBerlinDate locks the Europe/Berlin localisation:
// 22:30 UTC on 2026-05-31 is already 00:30 on 2026-06-01 in CEST, so the
// date segment must roll over.
func TestAutoSubmissionTitleBerlinDate(t *testing.T) {
	lateUTC := time.Date(2026, 5, 31, 22, 30, 0, 0, time.UTC)
	got := AutoSubmissionTitle(lateUTC, "Bayer AG", projectSide("claimant"),
		[]models.Party{party("Novartis", "Beklagte")}, proceeding("UPC", "upc.inf.cfi"))
	want := "2026-06-01 Bayer AG ./. UPC ./. Novartis"
	if got != want {
		t.Errorf("AutoSubmissionTitle (late UTC) = %q, want %q", got, want)
	}
}

func TestSubmissionForumShort(t *testing.T) {
	cases := []struct {
		pt   *models.ProceedingType
		want string
	}{
		{nil, ""},
		{proceeding("UPC", "upc.inf.cfi"), "UPC"},
		{proceeding("EPA", "epa.opp.opd"), "EPA"},
		{proceeding("DPMA", "dpma.opp.dpma"), "DPMA"},
		{proceeding("DE", "de.inf.lg"), "LG"},
		{proceeding("DE", "de.inf.olg"), "OLG"},
		{proceeding("DE", "de.inf.bgh"), "BGH"},
		{proceeding("DE", "de.null.bpatg"), "BPatG"},
		{proceeding("DE", "de.null.bgh"), "BGH"},
		{proceeding("DE", "de.foo.amtsgericht"), "AMTSGERICHT"}, // unknown court → uppercased tail
		{proceeding("de", "de.inf.lg"), "LG"},                   // lowercase jurisdiction folds
		{proceeding("", ""), ""},                                // no jurisdiction
	}
	for _, c := range cases {
		if got := submissionForumShort(c.pt); got != c.want {
			t.Errorf("submissionForumShort(%+v) = %q, want %q", c.pt, got, c.want)
		}
	}
}

func TestSubmissionOpponentName(t *testing.T) {
	claimantA := party("Acme", "Klägerin")
	defendantB := party("Novartis", "Beklagte")
	other := party("Streithelfer X", "Streithelfer")

	cases := []struct {
		name    string
		parties []models.Party
		ourSide string
		want    string
	}{
		{"active → first defendant", []models.Party{claimantA, defendantB}, "claimant", "Novartis"},
		{"reactive → first claimant", []models.Party{claimantA, defendantB}, "respondent", "Acme"},
		{"applicant (active) → defendant", []models.Party{defendantB}, "applicant", "Novartis"},
		{"appellant (active) → defendant", []models.Party{defendantB}, "appellant", "Novartis"},
		{"defendant (reactive) → claimant", []models.Party{claimantA}, "defendant", "Acme"},
		{"unknown side → none", []models.Party{claimantA, defendantB}, "third_party", ""},
		{"empty side → none", []models.Party{claimantA, defendantB}, "", ""},
		{"no opposing party → none", []models.Party{claimantA, other}, "claimant", ""},
		{"opposing bucket only 'other' → none", []models.Party{other}, "respondent", ""},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			if got := submissionOpponentName(c.parties, c.ourSide); got != c.want {
				t.Errorf("submissionOpponentName = %q, want %q", got, c.want)
			}
		})
	}
}

func TestUniqueDraftName(t *testing.T) {
	cases := []struct {
		name     string
		base     string
		existing []string
		want     string
	}{
		{"free", "2026-05-31 Bayer AG ./. UPC", nil, "2026-05-31 Bayer AG ./. UPC"},
		{"first clash → (2)", "2026-05-31 Bayer AG ./. UPC",
			[]string{"2026-05-31 Bayer AG ./. UPC"}, "2026-05-31 Bayer AG ./. UPC (2)"},
		{"two clash → (3)", "2026-05-31 Bayer AG ./. UPC",
			[]string{"2026-05-31 Bayer AG ./. UPC", "2026-05-31 Bayer AG ./. UPC (2)"},
			"2026-05-31 Bayer AG ./. UPC (3)"},
		{"gap reused → (2)", "X",
			[]string{"X", "X (3)"}, "X (2)"},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			if got := uniqueDraftName(c.base, c.existing); got != c.want {
				t.Errorf("uniqueDraftName = %q, want %q", got, c.want)
			}
		})
	}
}

func TestNextDraftName(t *testing.T) {
	cases := []struct {
		name     string
		existing []string
		lang     string
		want     string
	}{
		{"empty de", nil, "de", "Entwurf 1"},
		{"empty en", nil, "en", "Draft 1"},
		{"highest+1", []string{"Entwurf 1", "Entwurf 3"}, "de", "Entwurf 4"},
		{"ignores foreign names", []string{"2026-05-31 Bayer AG"}, "de", "Entwurf 1"},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			if got := nextDraftName(c.existing, c.lang); got != c.want {
				t.Errorf("nextDraftName = %q, want %q", got, c.want)
			}
		})
	}
}
