#include "HyperTwistRecognition/HyperTwistSpeechClient.h"

#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"

namespace HyperTwistMockSpeechClientInternal
{
	FString ResolveTranscriptText(
		const FHyperTwistSpeechSessionConfig* SessionConfig,
		const FHyperTwistSpeechUtteranceEnvelope& Utterance
	)
	{
		const FString NormalizedAudioRef = Utterance.AudioRef.ToLower();
		if (NormalizedAudioRef.Contains(TEXT("repeat-case")))
		{
			return TEXT("repeat case");
		}
		if (NormalizedAudioRef.Contains(TEXT("next-case")))
		{
			return TEXT("next case");
		}
		if (NormalizedAudioRef.Contains(TEXT("ready-coach")))
		{
			return TEXT("ready coach");
		}

		if (Utterance.LocalCommandHints.Num() > 0 && !Utterance.LocalCommandHints[0].IsEmpty())
		{
			return Utterance.LocalCommandHints[0];
		}

		if (SessionConfig != nullptr && SessionConfig->CommandHints.Num() > 0 && !SessionConfig->CommandHints[0].IsEmpty())
		{
			return SessionConfig->CommandHints[0];
		}

		return TEXT("ready coach");
	}

	TArray<FString> BuildRetrievedHints(const FHyperTwistSpeechSessionConfig* SessionConfig)
	{
		TArray<FString> RetrievedHints;
		if (SessionConfig == nullptr
			|| !SessionConfig->OrchestrationProfile.bEnableRetrievedHintAugmentation)
		{
			return RetrievedHints;
		}

		const int32 MaxHintCount =
			FMath::Max(SessionConfig->OrchestrationProfile.MaxRetrievedHintCount, 0);
		for (const FString& CommandHint : SessionConfig->CommandHints)
		{
			if (!CommandHint.IsEmpty())
			{
				RetrievedHints.Add(CommandHint);
			}
			if (RetrievedHints.Num() >= MaxHintCount)
			{
				break;
			}
		}

		return RetrievedHints;
	}
}

bool UHyperTwistMockSpeechClient::OpenSpeechSession(const FHyperTwistSpeechSessionConfig& Config, FString& OutError)
{
	OutError.Reset();

	if (!Config.IsStructurallyValid())
	{
		OutError = TEXT("speech-session-config-invalid");
		return false;
	}

	SessionConfigs.Add(Config.SessionId, Config);
	SessionUtteranceCounts.Add(Config.SessionId, 0);
	return true;
}

FHyperTwistSpeechTranscriptResult UHyperTwistMockSpeechClient::TranscribeSpeechUtterance(
	const FHyperTwistSpeechUtteranceEnvelope& Utterance
)
{
	FHyperTwistSpeechTranscriptResult Result = UHyperTwistContractLibrary::MakeMockSpeechTranscriptResult();
	Result.SessionId = Utterance.SessionId;
	Result.UtteranceId = Utterance.UtteranceId;

	const FHyperTwistSpeechSessionConfig* SessionConfig = SessionConfigs.Find(Utterance.SessionId);
	if (SessionConfig == nullptr)
	{
		Result.Warnings.Add(TEXT("session-not-open"));
		return Result;
	}

	const FString TranscriptText =
		HyperTwistMockSpeechClientInternal::ResolveTranscriptText(SessionConfig, Utterance);

	SessionUtteranceCounts.Add(Utterance.SessionId, SessionUtteranceCounts.FindRef(Utterance.SessionId) + 1);

	Result.LanguageCode = SessionConfig->LanguageMode.Equals(TEXT("auto"), ESearchCase::IgnoreCase)
		? TEXT("en")
		: SessionConfig->LanguageMode;
	Result.TaskKind = SessionConfig->TaskKind;
	Result.GrammarProfileId = SessionConfig->GrammarProfileId;
	Result.bIsFinal = true;
	Result.TranscriptText = TranscriptText;
	Result.Segments.Reset();
	Result.OrchestrationProfileId = SessionConfig->OrchestrationProfile.OrchestrationProfileId;
	Result.ServiceLaneId = SessionConfig->OrchestrationProfile.ServiceLaneId;
	Result.RequestedBatchSize = SessionConfig->OrchestrationProfile.PreferredBatchSize;
	Result.ProcessedClipCount = 1;
	Result.AppliedBatchCollectionWindowMs =
		SessionConfig->OrchestrationProfile.BatchCollectionWindowMs;
	Result.bUsedBatchedInference = SessionConfig->OrchestrationProfile.bEnableBatchedInference;
	Result.bWordTimestampsRequested = SessionConfig->OrchestrationProfile.bEnableWordTimestamps;
	Result.bConditionedOnPreviousText = SessionConfig->OrchestrationProfile.bConditionOnPreviousText;
	Result.AppliedPromptRoutingModeId =
		SessionConfig->OrchestrationProfile.PromptRoutingModeId;
	Result.AppliedRetrievalContextLaneId =
		SessionConfig->OrchestrationProfile.RetrievalContextLaneId;
	Result.LanguageProbability = SessionConfig->OrchestrationProfile.bEnableLanguageDetection ? 0.97f : 1.0f;
	Result.AppliedPrefixHint = SessionConfig->OrchestrationProfile.PrefixHint;
	Result.AppliedHotwordHints = SessionConfig->OrchestrationProfile.HotwordHints;
	Result.AppliedRetrievedHints =
		HyperTwistMockSpeechClientInternal::BuildRetrievedHints(SessionConfig);
	Result.RetrievedHintCount = Result.AppliedRetrievedHints.Num();
	Result.bUsedRetrievedHintAugmentation = Result.RetrievedHintCount > 0;
	Result.bUsedPromptHints =
		!Result.AppliedPrefixHint.IsEmpty()
		|| Result.AppliedHotwordHints.Num() > 0
		|| Result.bUsedRetrievedHintAugmentation;

	FHyperTwistSpeechTranscriptSegment Segment;
	Segment.SegmentOrdinal = 0;
	Segment.Text = TranscriptText;
	Segment.StartMs = FMath::Max(0, Utterance.SpeechStartMs);
	Segment.EndMs = FMath::Max(Segment.StartMs, Utterance.SpeechEndMs);
	Segment.Confidence = 0.93f;
	Segment.NoSpeechProbability = 0.05f;
	Segment.bGrammarConstrained = !SessionConfig->GrammarProfileId.IsEmpty();
	Result.Segments.Add(Segment);

	return Result;
}

bool UHyperTwistMockSpeechClient::CloseSpeechSession(const FString& SessionId, FString& OutError)
{
	OutError.Reset();

	if (!SessionConfigs.Contains(SessionId))
	{
		OutError = TEXT("session-not-open");
		return false;
	}

	SessionConfigs.Remove(SessionId);
	SessionUtteranceCounts.Remove(SessionId);
	return true;
}

FHyperTwistSpeechServiceHealth UHyperTwistMockSpeechClient::GetSpeechServiceHealth() const
{
	FHyperTwistSpeechServiceHealth Health = UHyperTwistContractLibrary::MakeMockSpeechServiceHealth();
	Health.ProviderLabel = TEXT("mock");
	Health.ServiceEndpoint = TEXT("in-process://mock");
	Health.Capabilities.AddUnique(TEXT("offline-transcribe"));
	Health.Capabilities.AddUnique(TEXT("microphoneCaptureShell"));
	Health.Capabilities.AddUnique(TEXT("devicePermissionShell"));
	Health.Capabilities.AddUnique(TEXT("captureRouteReadinessShell"));
	Health.Capabilities.AddUnique(TEXT("captureRouteRetry"));
	Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow"));
	Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff"));
	Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
	Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow"));
	Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflowShell"));
	Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl"));
	Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment"));
	Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipSummaryCard"));
	Health.Capabilities.AddUnique(TEXT("captureRoutePreparationCard"));
	Health.Capabilities.AddUnique(TEXT("captureRouteSessionReopenCard"));
	Health.Capabilities.AddUnique(TEXT("reopenSpeechSession"));
	Health.Capabilities.AddUnique(TEXT("streamingPreview"));
	Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit"));
	Health.Capabilities.AddUnique(TEXT("batchedTranscribe"));
	Health.Capabilities.AddUnique(TEXT("batchWindowTuning"));
	Health.Capabilities.AddUnique(TEXT("wordTimestamps"));
	Health.Capabilities.AddUnique(TEXT("languageDetection"));
	Health.Capabilities.AddUnique(TEXT("promptHints"));
	Health.Capabilities.AddUnique(TEXT("promptRoutingProfiles"));
	Health.Capabilities.AddUnique(TEXT("retrievalHintAugmentation"));
	Health.Capabilities.AddUnique(TEXT("providerRoutingPolicy"));
	Health.Capabilities.AddUnique(TEXT("providerRouteSelection"));
	Health.Capabilities.AddUnique(TEXT("workflowPolicyState"));
	Health.Capabilities.AddUnique(TEXT("downloadableModelPayloadCustody"));
	Health.Capabilities.AddUnique(TEXT("payloadIntegrityReview"));
	Health.Capabilities.AddUnique(TEXT("normalizedUsageEvents"));
	Health.Capabilities.AddUnique(TEXT("normalizedCostEvents"));
	Health.Capabilities.AddUnique(TEXT("quotaRateLimitState"));
	Health.Capabilities.AddUnique(TEXT("providerUsageCostDashboardShell"));
	Health.Capabilities.AddUnique(TEXT("providerQuotaEscalationBanner"));
	Health.Capabilities.AddUnique(TEXT("providerRouteCostDiagnostics"));
	Health.Capabilities.AddUnique(TEXT("providerUsageCostHistoryShell"));
	Health.Capabilities.AddUnique(TEXT("providerUsageCostExportPreview"));
	Health.Capabilities.AddUnique(TEXT("providerUsageCostSummaryCopy"));
	Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell"));
	Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection"));
	Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview"));
	Health.Capabilities.AddUnique(TEXT("providerBillingSettlementShell"));
	Health.Capabilities.AddUnique(TEXT("providerInvoiceReconciliation"));
	Health.Capabilities.AddUnique(TEXT("providerSettlementReadinessReview"));
	Health.Capabilities.AddUnique(TEXT("providerSettlementExceptionShell"));
	Health.Capabilities.AddUnique(TEXT("providerExternalPortalHandoff"));
	Health.Capabilities.AddUnique(TEXT("providerSettlementExceptionReview"));
	Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1"));
	return Health;
}
