<commit_before>﻿namespace FotM.Apollo

open System
open System.Collections.Generic
open System.Web.Configuration
open System.Net
open FotM.Aether
open FotM.Data
open FotM.Hephaestus.Async
open FotM.Hephaestus.TraceLogging
open FotM.Hephaestus.Math
open FotM.Hephaestus.CollectionExtensions
open Newtonsoft.Json
open FotM.Aether.StorageIO

type ArmoryAgentMessage =
| UpdateArmory of region: string * bracket : string * storageLocation: Uri
| StopAgent

type ArmoryInfo(ladder : TeamInfo list, storageLocation: Uri) =

    member this.snapshotUrl = storageLocation

    member this.teams = 
        ladder
        |> Seq.filter(fun t -> not (isNull t.lastEntry.players))
        |> Seq.filter(fun t -> t.totalGames >= 3)
        |> Seq.sortBy(fun t -> -t.lastEntry.rating) 
        |> Seq.mapi (fun i t -> i+1, t)
        |> Seq.toArray

    member this.totalGames = this.teams |> Seq.sumBy(fun (rank, team) -> team.totalGames)

    member this.setups =
        this.teams
        |> Seq.groupBy (fun (rank, teamInfo) -> teamInfo.lastEntry.getClasses())
        |> Seq.map (fun (specs, group) -> 
            let totalGames = group |> Seq.sumBy(fun (rank, teamInfo) -> teamInfo.totalGames)
            let totalWins = group |> Seq.sumBy(fun (rank, teamInfo) -> teamInfo.totalWins)
            specs, totalGames, totalWins ./. totalGames)

type Repository() =
    let mutable armoryData = [] |> Map.ofSeq

    member this.update(newData) = armoryData <- newData

    member this.getArmory(region: string, bracket: string) =
        let data = armoryData
        data.TryFind(region.ToUpper(), bracket)

open EkonBenefits.FSharp.Dynamic
open Microsoft.AspNet.SignalR
open Microsoft.AspNet.SignalR.Hubs

[<HubName("playingNowHub")>]
type PlayingNowHub() =
    inherit Hub()

module PlayingNowUpdateManager =
    let clients = lazy GlobalHost.ConnectionManager.GetHubContext<PlayingNowHub>().Clients

    let notifyUpdateReady(region: string, bracket: string): unit =
        clients.Value.All ? updateReady(region, bracket)

module Main =

    let createArmoryAgent (initialData: Map<string*string, ArmoryInfo>) (repository: Repository) = Agent<ArmoryAgentMessage>.Start(fun agent ->

        let show =
            let divisions = 
                initialData
                |> Seq.map(fun kv -> sprintf "%A: %A" kv.Key kv.Value.teams.Length)
            System.String.Join(";", divisions)
                
        logInfo "ArmoryAgent started with initialData: %s total teams..." show
        repository.update initialData

        let rec loop (armories: Map<string*string, ArmoryInfo>) = async {
            let! msg = agent.Receive()

            match msg with
            | UpdateArmory(region, bracket, storageLocation) ->
                let updatedArmories =
                    try
                        let snapshot = fetch<TeamInfo list> storageLocation
                        let armoryInfo = ArmoryInfo(snapshot, storageLocation)
                        let updatedArmories = armories |> Map.add(region, bracket) armoryInfo

                        repository.update updatedArmories
                        PlayingNowUpdateManager.notifyUpdateReady(region, bracket)
                        updatedArmories
                    with
                    | ex -> 
                        logError "Exception while handling message for %s, %s: %A" region bracket ex
                        armories
                return! loop updatedArmories
            | StopAgent -> 
                logInfo "ArmoryAgent stopped."
        }

        loop initialData
    )

    let repository = Repository()

    let backfillFrom(storage: Storage) =
        let storageRoots = 
            [
                for region in Regions.all do
                for bracket in Brackets.all do
                yield region.code, bracket.url
            ]

        let dir r b = r + "/" + b

        storageRoots
        |> Seq.choose(fun (region, bracket) -> 
            let allBlobs = storage.allBlobs (dir region bracket)

            let last = 
                allBlobs 
                |> Array.rev 
                |> Seq.map(fun blob -> 
                    try
                        fetch<TeamInfo list> blob.Uri, blob.Uri
                    with
                    | ex -> 
                        blob.Delete()
                        [], Uri("")
                    )
                |> Seq.skipWhile(fun (teams, uri) -> teams.IsEmpty)
                |> Seq.tryFind(fun _ -> true)

            match last with
            | Some (teams, uri) -> 
                logInfo "Backfilling for %s, %s from %A" region bracket uri
                Some((region, bracket), ArmoryInfo(teams, uri))
            | None -> 
                logInfo "No data found for backfill of %s, %s" region bracket
                None)
        |> Map.ofSeq

    let storageConnectionString = WebConfigurationManager.ConnectionStrings.["Microsoft.Storage.ConnectionString"]
    let serviceBusConnectionString = WebConfigurationManager.ConnectionStrings.["Microsoft.ServiceBus.ConnectionString"]

    let OnStart(): unit = 
            
        let storage = Storage(GlobalSettings.teamLaddersContainer, storageConnectionString.ConnectionString)

        let backfillData = backfillFrom storage

        let armoryAgent = createArmoryAgent backfillData repository

        let serviceBus = ServiceBus(serviceBusConnectionString.ConnectionString)

        let subscriptionName = Dns.GetHostName()<commit_msg>Fixed message stealing by staging website.<commit_after>﻿namespace FotM.Apollo

open System
open System.Collections.Generic
open System.Web.Configuration
open System.Net
open FotM.Aether
open FotM.Data
open FotM.Hephaestus.Async
open FotM.Hephaestus.TraceLogging
open FotM.Hephaestus.Math
open FotM.Hephaestus.CollectionExtensions
open Newtonsoft.Json
open FotM.Aether.StorageIO

type ArmoryAgentMessage =
| UpdateArmory of region: string * bracket : string * storageLocation: Uri
| StopAgent

type ArmoryInfo(ladder : TeamInfo list, storageLocation: Uri) =

    member this.snapshotUrl = storageLocation

    member this.teams = 
        ladder
        |> Seq.filter(fun t -> not (isNull t.lastEntry.players))
        |> Seq.filter(fun t -> t.totalGames >= 3)
        |> Seq.sortBy(fun t -> -t.lastEntry.rating) 
        |> Seq.mapi (fun i t -> i+1, t)
        |> Seq.toArray

    member this.totalGames = this.teams |> Seq.sumBy(fun (rank, team) -> team.totalGames)

    member this.setups =
        this.teams
        |> Seq.groupBy (fun (rank, teamInfo) -> teamInfo.lastEntry.getClasses())
        |> Seq.map (fun (specs, group) -> 
            let totalGames = group |> Seq.sumBy(fun (rank, teamInfo) -> teamInfo.totalGames)
            let totalWins = group |> Seq.sumBy(fun (rank, teamInfo) -> teamInfo.totalWins)
            specs, totalGames, totalWins ./. totalGames)

type Repository() =
    let mutable armoryData = [] |> Map.ofSeq

    member this.update(newData) = armoryData <- newData

    member this.getArmory(region: string, bracket: string) =
        let data = armoryData
        data.TryFind(region.ToUpper(), bracket)

open EkonBenefits.FSharp.Dynamic
open Microsoft.AspNet.SignalR
open Microsoft.AspNet.SignalR.Hubs

[<HubName("playingNowHub")>]
type PlayingNowHub() =
    inherit Hub()

module PlayingNowUpdateManager =
    let clients = lazy GlobalHost.ConnectionManager.GetHubContext<PlayingNowHub>().Clients

    let notifyUpdateReady(region: string, bracket: string): unit =
        clients.Value.All ? updateReady(region, bracket)

module Main =

    let createArmoryAgent (initialData: Map<string*string, ArmoryInfo>) (repository: Repository) = Agent<ArmoryAgentMessage>.Start(fun agent ->

        let show =
            let divisions = 
                initialData
                |> Seq.map(fun kv -> sprintf "%A: %A" kv.Key kv.Value.teams.Length)
            System.String.Join(";", divisions)
                
        logInfo "ArmoryAgent started with initialData: %s total teams..." show
        repository.update initialData

        let rec loop (armories: Map<string*string, ArmoryInfo>) = async {
            let! msg = agent.Receive()

            match msg with
            | UpdateArmory(region, bracket, storageLocation) ->
                let updatedArmories =
                    try
                        let snapshot = fetch<TeamInfo list> storageLocation
                        let armoryInfo = ArmoryInfo(snapshot, storageLocation)
                        let updatedArmories = armories |> Map.add(region, bracket) armoryInfo

                        repository.update updatedArmories
                        PlayingNowUpdateManager.notifyUpdateReady(region, bracket)
                        updatedArmories
                    with
                    | ex -> 
                        logError "Exception while handling message for %s, %s: %A" region bracket ex
                        armories
                return! loop updatedArmories
            | StopAgent -> 
                logInfo "ArmoryAgent stopped."
        }

        loop initialData
    )

    let repository = Repository()

    let backfillFrom(storage: Storage) =
        let storageRoots = 
            [
                for region in Regions.all do
                for bracket in Brackets.all do
                yield region.code, bracket.url
            ]

        let dir r b = r + "/" + b

        storageRoots
        |> Seq.choose(fun (region, bracket) -> 
            let allBlobs = storage.allBlobs (dir region bracket)

            let last = 
                allBlobs 
                |> Array.rev 
                |> Seq.map(fun blob -> 
                    try
                        fetch<TeamInfo list> blob.Uri, blob.Uri
                    with
                    | ex -> 
                        blob.Delete()
                        [], Uri("")
                    )
                |> Seq.skipWhile(fun (teams, uri) -> teams.IsEmpty)
                |> Seq.tryFind(fun _ -> true)

            match last with
            | Some (teams, uri) -> 
                logInfo "Backfilling for %s, %s from %A" region bracket uri
                Some((region, bracket), ArmoryInfo(teams, uri))
            | None -> 
                logInfo "No data found for backfill of %s, %s" region bracket
                None)
        |> Map.ofSeq

    let storageConnectionString = WebConfigurationManager.ConnectionStrings.["Microsoft.Storage.ConnectionString"]
    let serviceBusConnectionString = WebConfigurationManager.ConnectionStrings.["Microsoft.ServiceBus.ConnectionString"]

    let OnStart(): unit = 
            
        let storage = Storage(GlobalSettings.teamLaddersContainer, storageConnectionString.ConnectionString)

        let backfillData = backfillFrom storage
        
        let armoryAgent = createArmoryAgent backfillData repository

        let serviceBus = ServiceBus(serviceBusConnectionString.ConnectionString)

        // TODO: fix for local
        let subscriptionName = Dns.GetHostName() + Guid.NewGuid().ToString().Substring(0, 4)
{'fraction_non_alphanumeric': 0.08981765661671783, 'fraction_numerical': 0.0005416140097490522, 'mean_word_length': 1.9654710920770877, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 99, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2713751', 'n_tokens_mistral': 3148, 'n_tokens_neox': 2908, 'n_words': 750}