package adminconsole

import "html/template"

// The *View types are the display models the gin handlers fill and the templates
// render. Time values are pre-formatted to strings by the handlers so the
// templates stay logic-free.

// Pager is the shared list pagination state.
type Pager struct {
	Page     int
	PageSize int
	Total    int
	HasPrev  bool
	HasNext  bool
	PrevPage int
	NextPage int
}

// NewPager builds the pagination state for a 1-based page of pageSize over total
// items.
func NewPager(page, pageSize, total int) Pager {
	if page < 1 {
		page = 1
	}
	p := Pager{Page: page, PageSize: pageSize, Total: total, PrevPage: page - 1, NextPage: page + 1}
	p.HasPrev = page > 1
	p.HasNext = page*pageSize < total
	return p
}

// VariantVersions lists the dictionary versions resident for one variant.
type VariantVersions struct {
	Variant  string
	Latest   string
	Versions []string
}

// DashboardView is the landing-page summary.
type DashboardView struct {
	Accounts       int
	Games          int
	ActiveGames    int
	OpenComplaints int
	OpenFeedback   int
	PendingChanges int
	// ActiveVersion is the dictionary version new games pin (the persisted active
	// version), distinct from the per-variant resident versions.
	ActiveVersion string
	Variants      []VariantVersions
}

// UsersView is the paginated account list.
type UsersView struct {
	Items []UserRow
	Pager Pager
	// Robots is the active people/robots toggle; NameMask/ExternalIDMask are the current
	// glob filters; FilterQuery is those URL-encoded for the pager links. It is an
	// already-escaped query fragment (url.Values.Encode), so it is typed template.URL to
	// be emitted verbatim — interpolated as a plain string it would have its "=" and "&"
	// percent-encoded again by the contextual escaper.
	Robots         bool
	NameMask       string
	ExternalIDMask string
	FilterQuery    template.URL
}

// UserRow is one account row in the list. MoveMin/Avg/Max are the account's
// pre-formatted move-duration summary (empty when it has no timed move);
// FlaggedHighRate marks the soft high-rate badge.
type UserRow struct {
	ID              string
	DisplayName     string
	Kind            string
	Language        string
	Guest           bool
	FlaggedHighRate bool
	CreatedAt       string
	HasMoveStats    bool
	MoveMin         string
	MoveAvg         string
	MoveMax         string
}

// MessagesView is the paginated chat-message moderation list. NameMask/ExtMask are the
// current sender glob filters; GameID/UserID pin the list to one game / sender (set from a
// game or user card); FilterQuery is the active filters URL-encoded for the pager and CSV
// links — an already-escaped query fragment, hence template.URL so it is not re-encoded
// inside the link (see UsersView.FilterQuery).
type MessagesView struct {
	Items       []MessageRow
	Pager       Pager
	NameMask    string
	ExtMask     string
	GameID      string
	UserID      string
	UnreadOnly  bool
	FilterQuery template.URL
}

// MessageRow is one chat message in the moderation list: its sender (linked to the user
// card), source, IP, body, game (linked to the game card), time, and whether it is still
// unread by at least one recipient.
type MessageRow struct {
	ID         string
	SenderID   string
	SenderName string
	Source     string
	IP         string
	Body       string
	GameID     string
	CreatedAt  string
	Unread     bool
}

// ChatMessageDetailView is one chat message with its per-seat read breakdown, for the
// console message card.
type ChatMessageDetailView struct {
	ID         string
	GameID     string
	SenderID   string
	SenderName string
	Source     string
	Kind       string
	Body       string
	IP         string
	CreatedAt  string
	Unread     bool
	Seats      []ChatSeatStatusRow
}

// ChatSeatStatusRow is one seat's read status on the message card: the seat index, the
// occupant (linked to the user card) and its role ("sender", "read" or "unread").
type ChatSeatStatusRow struct {
	Seat        int
	AccountID   string
	DisplayName string
	Role        string
}

// UserDetailView is one account with its stats, identities and recent games.
type UserDetailView struct {
	ID                     string
	DisplayName            string
	Language               string
	TimeZone               string
	Guest                  bool
	NotificationsInAppOnly bool
	PaidAccount            bool
	// MergedInto is the primary account id when this account has been retired by a
	// merge, or empty for a live account.
	MergedInto string
	// FlaggedHighRateAt is the pre-formatted soft high-rate marker timestamp,
	// empty for an unflagged account; the card shows it with the Clear action.
	FlaggedHighRateAt string
	HintBalance       int
	// HintGrantMax is the per-grant cap the operator's "add hints" form enforces (it mirrors the
	// server's maxHintGrant), passed through so the policy value lives in one place.
	HintGrantMax     int
	CreatedAt        string
	HasStats         bool
	Stats            StatsRow
	Identities       []IdentityRow
	Games            []GameRow
	TelegramID       string
	ConnectorEnabled bool
	// MoveChart is the pre-rendered inline SVG of the account's per-move-number think
	// time (min/mean/max), empty when the account has no timed move.
	MoveChart template.HTML
	// Suspension is the account's current manual-block state, shown with the block/unblock
	// form; Reasons is the operator-editable reason picklist offered in the block form.
	Suspension SuspensionView
	Reasons    []ReasonOption
	// Roles is the account's current roles (each revocable); KnownRoles is the set the
	// grant form offers. The first role is the feedback ban (see internal/account).
	Roles      []string
	KnownRoles []string
	// Blocks, BlockedBy and Friends are the social graph on the card: who this account has
	// blocked, who currently blocks it, and its mutual friendships — each cross-linked to the
	// other account with the date it happened. They are the full truth; the asymmetric block
	// suppression that hides relationships from players never applies to the console.
	Blocks    []RelationRow
	BlockedBy []RelationRow
	Friends   []RelationRow
}

// RelationRow is one cross-linked account in the user card's blocks / blocked-by / friends
// lists: the other account's id (the link target), its display name, and the pre-formatted date.
type RelationRow struct {
	AccountID   string
	DisplayName string
	Date        string
}

// SuspensionView is an account's current manual-block state shown on the user card: whether it
// is blocked, whether the block is permanent, the pre-formatted expiry (empty when permanent),
// when it was applied, and the reason snapshot in both languages (empty when none was cited).
type SuspensionView struct {
	Blocked   bool
	Permanent bool
	Until     string
	BlockedAt string
	ReasonEn  string
	ReasonRu  string
}

// ReasonOption is one suspension-reason picklist entry offered in the block form's dropdown.
type ReasonOption struct {
	ID     string
	TextEn string
	TextRu string
}

// StatsRow is an account's lifetime statistics.
type StatsRow struct {
	Wins          int
	Losses        int
	Draws         int
	MaxGamePoints int
	MaxWordPoints int
	Moves         int
	HintsUsed     int
}

// IdentityRow is one platform/email identity of an account.
type IdentityRow struct {
	Kind       string
	ExternalID string
	Confirmed  bool
	CreatedAt  string
}

// GameRow is one game row in a list.
type GameRow struct {
	ID        string
	Variant   string
	Status    string
	Players   int
	UpdatedAt string
	// VsAI marks an honest-AI game (rendered as 🤖 in the list's AI column).
	VsAI bool
}

// GamesView is the paginated games list, optionally filtered by status.
type GamesView struct {
	Items  []GameRow
	Status string
	Pager  Pager
}

// GameDetailView is one game with its seats.
type GameDetailView struct {
	ID          string
	Variant     string
	DictVersion string
	Status      string
	Players     int
	ToMove      int
	EndReason   string
	MoveCount   int
	CreatedAt   string
	UpdatedAt   string
	FinishedAt  string
	// VsAI marks an honest-AI game (shown as a 🤖 flag in the summary).
	VsAI  bool
	Seats []SeatRow
	// HasRobot is true when any seat is a robot, gating the robot-target caption;
	// RobotTargetPct is the configured global play-to-win rate, in percent.
	HasRobot       bool
	RobotTargetPct int
	// ReplayJSON is the game-replay payload (board, seats, per-step racks/scores/bag) the
	// game_detail page feeds to its vanilla-JS stepper; HasReplay gates the replay section.
	ReplayJSON template.JS
	HasReplay  bool
	// SetupDraws is the first-move draw — one row per tile drawn (docs/ARCHITECTURE.md §6) —
	// and FirstMover is the resolved name of the seat-0 player the draw elected.
	SetupDraws []SetupDrawRow
	FirstMover string
}

// SetupDrawRow is one tile drawn in the first-move seeding (docs/ARCHITECTURE.md §6): the
// round, the player (Name/AccountID, or "(opponent)" with an empty AccountID for an
// auto-match synthetic draw not yet back-filled), the drawn letter (upper-cased; "?" for a
// blank) and its draw rank.
type SetupDrawRow struct {
	Round     int
	Name      string
	AccountID string
	Letter    string
	Blank     bool
	Rank      int
}

// SeatRow is one seat of a game. For a robot seat (IsRobot) RobotIntent is the game's
// deterministic play-to-win decision ("play to win"/"play to lose"), and NextMove is the
// scheduled next-move ETA shown only while it is that robot's turn in an active game.
type SeatRow struct {
	Seat        int
	DisplayName string
	AccountID   string
	Score       int
	HintsUsed   int
	Winner      bool
	IsRobot     bool
	RobotIntent string
	NextMove    string
}

// ComplaintsView is the paginated complaint review queue.
type ComplaintsView struct {
	Items  []ComplaintRow
	Status string
	Pager  Pager
}

// ComplaintRow is one complaint row in the queue.
type ComplaintRow struct {
	ID          string
	Word        string
	Variant     string
	WasValid    bool
	Status      string
	Disposition string
	CreatedAt   string
}

// ComplaintDetailView is one complaint with its resolution state and form.
type ComplaintDetailView struct {
	ID             string
	Word           string
	Variant        string
	DictVersion    string
	WasValid       bool
	Note           string
	Status         string
	Disposition    string
	ResolutionNote string
	CreatedAt      string
	ResolvedAt     string
	GameID         string
	Resolved       bool
}

// DictionaryView lists the resident versions per variant, the active version new
// games pin, and the pending wordlist changes from accepted complaints.
type DictionaryView struct {
	// ActiveVersion is the dictionary version new games pin; the update form sets it.
	ActiveVersion string
	Variants      []VariantVersions
	Changes       []DictChangeRow
}

// DictChangeRow is one pending wordlist edit.
type DictChangeRow struct {
	Variant    string
	Word       string
	Action     string
	ResolvedAt string
}

// DictionaryPreviewView is the second step of a dictionary update: the version
// parsed from the uploaded archive (editable before confirming), the staging token
// that names the uploaded files on disk, the active version the diff is against, and
// the per-variant word diff.
type DictionaryPreviewView struct {
	Version       string
	Token         string
	ActiveVersion string
	Variants      []VariantDiffRow
}

// VariantDiffRow summarises one variant's word diff in the update preview.
// AddedCount/RemovedCount are the full totals; AddedSample/RemovedSample are the
// first words shown (capped); AddedTruncated/RemovedTruncated mark a capped list;
// LargeRemoval flags a removal large enough to warrant caution before confirming.
type VariantDiffRow struct {
	Variant          string
	AddedCount       int
	RemovedCount     int
	AddedSample      []string
	RemovedSample    []string
	AddedTruncated   bool
	RemovedTruncated bool
	LargeRemoval     bool
}

// BroadcastView is the operator-broadcast form page.
type BroadcastView struct {
	ConnectorEnabled bool
}

// ThrottledView is the rate-limit observability page: the temporary IP bans the
// gateway is currently enforcing, the recent gateway-reported throttle episodes
// (in-memory, reset on restart) and the accounts currently carrying the high-rate
// flag. FlagThreshold and FlagWindow caption the active auto-flag tuning.
type ThrottledView struct {
	Bans          []BanRow
	Episodes      []ThrottleEpisodeRow
	Flagged       []FlaggedAccountRow
	FlagThreshold int
	FlagWindow    string
}

// BanRow is one temporary IP ban the gateway is enforcing, with its reason and its
// since/expiry timestamps; the row carries an unban action.
type BanRow struct {
	IP      string
	Reason  string
	Since   string
	Expires string
}

// ThrottleEpisodeRow is one recently throttled limiter key. UserID links to the
// user card and is set only for the user class (the other classes key by IP).
type ThrottleEpisodeRow struct {
	Class     string
	Key       string
	UserID    string
	Rejected  int
	FirstSeen string
	LastSeen  string
}

// FlaggedAccountRow is one account carrying the high-rate flag.
type FlaggedAccountRow struct {
	ID          string
	DisplayName string
	FlaggedAt   string
}

// ReasonsView is the suspension-reason picklist management page: every editable reason entry.
type ReasonsView struct {
	Items []ReasonRow
}

// ReasonRow is one editable suspension-reason entry, with its English and Russian text.
type ReasonRow struct {
	ID        string
	TextEn    string
	TextRu    string
	CreatedAt string
}

// MessageView is the result page shown after a POST action.
type MessageView struct {
	Heading string
	Body    string
	Back    string
}

// BannersView is the advertising-campaign list page.
type BannersView struct {
	Items []BannerCampaignRow
}

// BannerCampaignRow is one campaign in the list. Window is a human-readable
// validity window ("perpetual" for the default); ActiveNow reports whether it
// would rotate right now (enabled and within its window).
type BannerCampaignRow struct {
	ID        string
	Name      string
	Weight    int
	IsDefault bool
	Enabled   bool
	Window    string
	Messages  int
	ActiveNow bool
}

// BannerDetailView is the campaign detail/edit page. StartsAt/EndsAt are the
// "YYYY-MM-DDTHH:MM" (UTC) values for the datetime-local inputs, empty when open.
type BannerDetailView struct {
	ID        string
	Name      string
	Weight    int
	IsDefault bool
	Enabled   bool
	StartsAt  string
	EndsAt    string
	Messages  []BannerMessageRow
}

// BannerMessageRow is one bilingual message of a campaign. First/Last drive the
// reorder buttons (disabled at the ends).
type BannerMessageRow struct {
	ID     string
	BodyEn string
	BodyRu string
	First  bool
	Last   bool
}

// BannerSettingsView is the global display-timings form.
type BannerSettingsView struct {
	HoldMs         int
	EdgePauseMs    int
	ScrollPxPerSec int
	FadeOutMs      int
	GapMs          int
	FadeInMs       int
}

// FeedbackView is the paginated user-feedback queue. Status is the active
// unread/read/archived filter; NameMask/ExtMask are the sender glob filters;
// UserID pins the list to one account (the per-user link from /users);
// FilterQuery is the active filters URL-encoded for the pager links (already
// escaped, hence template.URL — see UsersView.FilterQuery).
type FeedbackView struct {
	Items       []FeedbackRow
	Status      string
	NameMask    string
	ExtMask     string
	UserID      string
	Pager       Pager
	FilterQuery template.URL
}

// FeedbackRow is one feedback message in the queue: its sender (linked to the user
// card), source, channel, whether it has an attachment / a reply, its state and
// time.
type FeedbackRow struct {
	ID            string
	AccountID     string
	SenderName    string
	Source        string
	Channel       string
	HasAttachment bool
	Read          bool
	Replied       bool
	Archived      bool
	CreatedAt     string
}

// FeedbackDetailView is one feedback message with its body, attachment, state and
// the reply / archive / delete forms. Body, AttachmentName, SenderName and IP are
// user-controlled and rendered as plain auto-escaped text. IsImage gates the inline
// <img> preview; Banned shows whether the sender already holds the feedback ban.
type FeedbackDetailView struct {
	ID         string
	AccountID  string
	SenderName string
	Source     string
	Channel    string
	// InterfaceLanguage is the sender's interface language (account preference).
	InterfaceLanguage string
	IP                string
	Body              string
	HasAttachment     bool
	AttachmentName    string
	IsImage           bool
	Read              bool
	Archived          bool
	Replied           bool
	ReplyBody         string
	RepliedAt         string
	CreatedAt         string
	Banned            bool
}
