package services

// Auto-naming for freshly-created submission drafts (t-paliad-352 /
// m/paliad#155). A new project-bound draft gets a sortable, legal-
// convention default title instead of the bare "Entwurf N" counter:
//
//	<YYYY-MM-DD> <ClientName> ./. <ForumShort> ./. <OpponentName>
//
// The date leads so drafts sort chronologically; " ./. " is the German
// legal shorthand for "gegen". The three identity segments are the
// client we act for, the forum the proceeding runs in, and the opposing
// party — exactly the trio m named ("CLIENTNAME / UPC / OPPONENTNAME").
//
// Missing-segment rule: any segment that resolves empty is dropped
// together with its leading separator, so a project without an opponent
// yet renders "2026-05-31 Bayer AG ./. UPC" (no trailing separator) and
// a project-less draft never reaches this path at all (it keeps the
// "Entwurf N" counter — see SubmissionDraftService.Create).
//
// v1 promotes this scheme into the pkg/nomen composition engine: the
// template lives as the submission_draft_title artifact's system-default
// Composition (see namegen.go, PRD §5.1) and the identity resolvers below
// stay as the value source. AutoSubmissionTitle is now a thin wrapper that
// renders that composition; the assembly logic (separators, missing-segment
// rules) is the engine's. Per-user / per-firm overrides (Slices 3–5) layer
// onto the artifact without touching this file.

import (
	"strings"
	"time"

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

// AutoSubmissionTitle assembles the auto-generated draft title from the
// resolved identity pieces. Pure and table-testable — every DB hop
// happens in the caller (SubmissionDraftService.autoNameForProject).
//
// clientName is passed separately because the client we act for is the
// root ancestor of the project tree, not a field on the draft's own
// project node; the caller walks the path to resolve it. ourSide and
// the proceeding type both come off the draft's project node, the
// parties hang directly off it.
//
// The date is always present (formatted in Europe/Berlin); the three
// identity segments are appended only when non-empty. Rendered through the
// submission_draft_title artifact (namegen.go).
func AutoSubmissionTitle(now time.Time, clientName string, project *models.Project, parties []models.Party, pt *models.ProceedingType) string {
	art := nameArtifacts[ArtifactSubmissionDraftTitle]
	resolve := submissionTitleResolver(now, clientName, project, parties, pt)
	return art.SystemDefault.Render(resolve, art.Target)
}

// submissionForumShort maps a proceeding type to the short forum label
// used in the auto-name. The jurisdiction is the forum for the
// supranational / office tracks (UPC, EPA, DPMA); German court
// proceedings disambiguate by the court that hears them (LG / OLG /
// BGH / BPatG), which is the tail segment of the proceeding code
// (de.inf.lg → LG, de.null.bpatg → BPatG). nil / unknown → "".
func submissionForumShort(pt *models.ProceedingType) string {
	if pt == nil {
		return ""
	}
	switch j := strings.ToUpper(strings.TrimSpace(derefString(pt.Jurisdiction))); j {
	case "":
		return ""
	case "DE":
		return germanCourtShort(pt.Code)
	default:
		// UPC / EPA / DPMA and any future jurisdiction are their own
		// forum label.
		return j
	}
}

// germanCourtShort returns the court abbreviation from the tail segment
// of a German proceeding code (the part after the last "."). Known
// courts get their canonical casing; anything else falls back to the
// uppercased tail so a new German proceeding still yields a label.
func germanCourtShort(code string) string {
	parts := strings.Split(code, ".")
	tail := strings.ToLower(strings.TrimSpace(parts[len(parts)-1]))
	switch tail {
	case "":
		return ""
	case "lg":
		return "LG"
	case "olg":
		return "OLG"
	case "bgh":
		return "BGH"
	case "bpatg":
		return "BPatG"
	default:
		return strings.ToUpper(tail)
	}
}

// submissionOpponentName picks the name of the primary opposing party
// given the side we act for. We act actively (claimant / applicant /
// appellant) → the opponent is on the defendant bucket; we act
// reactively (defendant / respondent) → the opponent is the claimant.
// An unknown / unset side (third_party, other, NULL) can't fix a
// posture, so no opponent is derived (the segment is omitted). The
// first party of the opposing bucket wins — PartyService.ListForProject
// orders by name, so the pick is deterministic for a given project.
func submissionOpponentName(parties []models.Party, ourSide string) string {
	var want string
	switch sidePosture(ourSide) {
	case "active":
		want = "defendant"
	case "reactive":
		want = "claimant"
	default:
		return ""
	}
	for i := range parties {
		if partyRoleBucket(parties[i].Role) == want {
			if n := strings.TrimSpace(parties[i].Name); n != "" {
				return n
			}
		}
	}
	return ""
}

// sidePosture folds the our_side sub-role vocabulary (t-paliad-222)
// down to the active / reactive axis. Returns "" for sides that have no
// clear posture (third_party, other) or an unset value.
func sidePosture(ourSide string) string {
	switch strings.ToLower(strings.TrimSpace(ourSide)) {
	case "claimant", "applicant", "appellant":
		return "active"
	case "defendant", "respondent":
		return "reactive"
	default:
		return ""
	}
}

// partyRoleBucket folds a party's free-text role into the
// claimant / defendant / other buckets. German and English spellings
// both fold in; everything else (Streithelfer, Patentinhaberin, …) is
// "other". Shared with addPartyVars so the two paths can't drift.
func partyRoleBucket(role *string) string {
	switch strings.ToLower(strings.TrimSpace(derefString(role))) {
	case "claimant", "kläger", "klaeger", "klägerin", "klaegerin":
		return "claimant"
	case "defendant", "beklagter", "beklagte":
		return "defendant"
	default:
		return "other"
	}
}
