package dto_admin

// The admin panel's view of sessions that are still running.
//
// Kept separate from the settlement DTOs on purpose: those describe money
// already billed and sitting in a review queue, these describe a live session
// with a clock on it. Different lifecycle, different refresh rate, and putting
// elapsed/remaining fields on every settled row would mean six always-zero
// columns on the busiest screen in the panel.

//------------------------------------------------
// Filters
//------------------------------------------------

type LiveConsultationFilter struct {
	Search string

	// REQUESTED | ACCEPTED | ONGOING | LIVE (all three). Not
	// settlement_status: that is NA on every live row, so filtering on it
	// would mean nothing.
	Status string

	Medium string

	AstrologerID uint
	UserID       uint

	// Only sessions whose ticks have gone quiet — the "what is stuck" view.
	StalledOnly bool

	// Only free chats.
	FreeOnly bool

	SortBy  string
	SortDir string

	Page  int
	Limit int
}

//------------------------------------------------
// List
//------------------------------------------------

type LiveConsultationItem struct {
	ConsultationID uint   `json:"consultation_id"`
	ConsultationNo string `json:"consultation_no"`

	UserID     uint   `json:"user_id"`
	UserName   string `json:"user_name"`
	UserMobile string `json:"user_mobile"`

	AstrologerID   uint   `json:"astrologer_id"`
	AstrologerName string `json:"astrologer_name"`

	Medium string `json:"medium"`
	Status string `json:"status"`

	IsFreeChat  bool `json:"is_free_chat"`
	FreeMinutes int  `json:"free_minutes"`

	ChannelName   string  `json:"channel_name"`
	RatePerMinute float64 `json:"rate_per_minute"`

	RequestedAt string `json:"requested_at,omitempty"`
	AcceptedAt  string `json:"accepted_at,omitempty"`
	StartedAt   string `json:"started_at,omitempty"`
	LastTickAt  string `json:"last_tick_at,omitempty"`

	// Computed against the clock on every read, not stored, so the list is
	// always current without anything having to write to it.
	ElapsedSeconds       int     `json:"elapsed_seconds"`
	Elapsed              string  `json:"elapsed"`
	RemainingSeconds     int     `json:"remaining_seconds"`
	MaxBillableSeconds   int     `json:"max_billable_seconds"`
	CurrentCharge        float64 `json:"current_charge"`
	SecondsSinceLastTick int     `json:"seconds_since_last_tick"`
	IsStalled            bool    `json:"is_stalled"`

	MessageCount int64 `json:"message_count"`

	CreatedAt string `json:"created_at"`
}

type LiveConsultationListResponse struct {
	Items []LiveConsultationItem `json:"items"`

	Page       int   `json:"page"`
	Limit      int   `json:"limit"`
	Total      int64 `json:"total"`
	TotalPages int   `json:"total_pages"`

	RequestedCount int `json:"requested_count"`
	AcceptedCount  int `json:"accepted_count"`
	OngoingCount   int `json:"ongoing_count"`
	StalledCount   int `json:"stalled_count"`

	// Money in flight across the matching rows: run up, but not yet taken
	// from anyone's wallet. It does not exist anywhere else in the panel.
	LiveCharge float64 `json:"live_charge"`
}

//------------------------------------------------
// Summary
//------------------------------------------------

// LiveSummaryResponse backs the panel header, which auto-refreshes. Separate
// from the list so that refresh does not re-run the joined, searched,
// paginated query every few seconds.
type LiveSummaryResponse struct {
	RequestedCount int `json:"requested_count"`
	AcceptedCount  int `json:"accepted_count"`
	OngoingCount   int `json:"ongoing_count"`
	StalledCount   int `json:"stalled_count"`

	FreeChatOngoingCount int `json:"free_chat_ongoing_count"`

	ChatCount  int `json:"chat_count"`
	AudioCount int `json:"audio_count"`
	VideoCount int `json:"video_count"`

	LiveCharge float64 `json:"live_charge"`

	LongestRunningSeconds int `json:"longest_running_seconds"`

	// Surfaces "tokens cannot be minted" on the dashboard rather than in a
	// support ticket three days later.
	AgoraConfigured bool `json:"agora_configured"`
	ChatEnabled     bool `json:"chat_enabled"`

	AsOf string `json:"as_of"`
}

//------------------------------------------------
// Detail
//------------------------------------------------

type LiveConsultationDetail struct {
	LiveConsultationItem

	UserEmail        string `json:"user_email"`
	AstrologerMobile string `json:"astrologer_mobile"`

	ConsulteeName       string `json:"consultee_name,omitempty"`
	ConsulteeBirthDate  string `json:"consultee_birth_date,omitempty"`
	ConsulteeBirthTime  string `json:"consultee_birth_time,omitempty"`
	ConsulteeBirthPlace string `json:"consultee_birth_place,omitempty"`
	ConsulteeGender     string `json:"consultee_gender,omitempty"`

	RingTimeoutSeconds int `json:"ring_timeout_seconds"`
	JoinTimeoutSeconds int `json:"join_timeout_seconds"`
	TickTimeoutSeconds int `json:"tick_timeout_seconds"`

	WarnedAt string `json:"warned_at,omitempty"`

	SettlementStatus string `json:"settlement_status"`

	// Never the token itself, and never the certificate. The panel needs to
	// know a channel exists, not to be able to join it — handing a live join
	// credential to a browser session is a different product.
	AgoraChannel string `json:"agora_channel"`

	History []SettlementLogItem `json:"history"`
}

//------------------------------------------------
// Force end
//------------------------------------------------

// ForceEndRequest closes sessions from the panel.
//
// Bulk with a Skipped list, and mandatory remarks, exactly like the settlement
// review actions: a force-end is the same class of decision, and somebody will
// have to explain it later.
type ForceEndRequest struct {
	ConsultationIDs []uint `json:"consultation_ids" binding:"required"`

	Remarks string `json:"remarks"`

	// false closes at zero duration without charging anyone — the "this
	// session never really happened" case. Defaults to true.
	Bill *bool `json:"bill"`
}

type ForceEndResponse struct {
	EndedCount int    `json:"ended_count"`
	EndedIDs   []uint `json:"ended_ids"`

	// Unknown or already-finished ids land here rather than failing the whole
	// call. One bad id must not stop the rest being closed.
	Skipped []SkippedConsultation `json:"skipped"`

	TotalBilled  float64 `json:"total_billed"`
	TotalEarning float64 `json:"total_earning"`
}

//------------------------------------------------
// Transcript
//------------------------------------------------

type TranscriptMessage struct {
	Sequence        int    `json:"sequence"`
	ClientMessageID string `json:"client_message_id"`
	SenderType      string `json:"sender_type"`
	SenderID        uint   `json:"sender_id"`
	SenderName      string `json:"sender_name"`
	MessageType     string `json:"message_type"`
	Body            string `json:"body"`
	SentAt          string `json:"sent_at"`
	UploadedBy      string `json:"uploaded_by"`
}

type TranscriptResponse struct {
	ConsultationID uint   `json:"consultation_id"`
	ConsultationNo string `json:"consultation_no"`

	Medium string `json:"medium"`

	UserID         uint   `json:"user_id"`
	UserName       string `json:"user_name"`
	AstrologerID   uint   `json:"astrologer_id"`
	AstrologerName string `json:"astrologer_name"`

	StartedAt string `json:"started_at,omitempty"`
	EndedAt   string `json:"ended_at,omitempty"`
	Duration  string `json:"duration"`

	MessageCount int64 `json:"message_count"`

	// NONE | PARTIAL (one side uploaded) | COMPLETE (both) | NOT_APPLICABLE.
	//
	// A chat with no rows is information, not an error: it means neither app
	// managed to flush its buffer.
	UploadStatus string   `json:"upload_status"`
	UploadedBy   []string `json:"uploaded_by"`

	Messages []TranscriptMessage `json:"messages"`

	Page       int   `json:"page"`
	Limit      int   `json:"limit"`
	Total      int64 `json:"total"`
	TotalPages int   `json:"total_pages"`
}

//------------------------------------------------
// Chat settings
//------------------------------------------------

type ChatSettingsResponse struct {
	ChatConsultationEnabled bool `json:"chat_consultation_enabled"`

	FreeChatEnabled             bool `json:"free_chat_enabled"`
	FreeChatMinutes             int  `json:"free_chat_minutes"`
	FreeChatMinConnectedSeconds int  `json:"free_chat_min_connected_seconds"`

	// Read-only. The rule is enforced by a query against the sessions
	// themselves plus a unique index, not by configuration — making it
	// switchable would change the shape of that query at runtime, which is
	// how you end up with a customer holding unlimited free chats.
	FreeChatScope string `json:"free_chat_scope"`

	ConsultationRingTimeoutSecs int `json:"consultation_ring_timeout_secs"`
	ConsultationJoinTimeoutSecs int `json:"consultation_join_timeout_secs"`
	ConsultationWarningSeconds  int `json:"consultation_warning_seconds"`
	ConsultationMaxMinutes      int `json:"consultation_max_minutes"`

	ChatTickTimeoutSecs         int `json:"chat_tick_timeout_secs"`
	ConsultationTickTimeoutSecs int `json:"consultation_tick_timeout_secs"`

	ConsultationAvailabilityStrict bool `json:"consultation_availability_strict"`

	ChatTranscriptWindowHours int `json:"chat_transcript_window_hours"`

	// Derived from the environment, never writable here. The certificate is a
	// signing key; whoever holds it can join any session, so it does not
	// belong in a settings screen or in systemflag.
	AgoraAppID                 string `json:"agora_app_id"`
	AgoraCertificateConfigured bool   `json:"agora_certificate_configured"`
	AgoraConfigured            bool   `json:"agora_configured"`

	// What the platform has spent on free chats. Finance will ask for this
	// the day the offer goes live.
	FreeChatClaimedCount  int64   `json:"free_chat_claimed_count"`
	FreeChatCostThisMonth float64 `json:"free_chat_cost_this_month"`
}

// UpdateChatSettingsRequest is a partial update: every field is a pointer, so
// a screen that only edits one value does not silently reset the rest.
type UpdateChatSettingsRequest struct {
	ChatConsultationEnabled *bool `json:"chat_consultation_enabled"`

	FreeChatEnabled             *bool `json:"free_chat_enabled"`
	FreeChatMinutes             *int  `json:"free_chat_minutes"`
	FreeChatMinConnectedSeconds *int  `json:"free_chat_min_connected_seconds"`

	ConsultationRingTimeoutSecs *int `json:"consultation_ring_timeout_secs"`
	ConsultationJoinTimeoutSecs *int `json:"consultation_join_timeout_secs"`
	ConsultationWarningSeconds  *int `json:"consultation_warning_seconds"`
	ConsultationMaxMinutes      *int `json:"consultation_max_minutes"`

	ChatTickTimeoutSecs *int `json:"chat_tick_timeout_secs"`

	ConsultationAvailabilityStrict *bool `json:"consultation_availability_strict"`

	ChatTranscriptWindowHours *int `json:"chat_transcript_window_hours"`
}
