modules/FeedProcessor/LSportsProcessor.psm1

using module '..\Enums.psm1'
using module '..\Helper\DateTimeHelper.psm1'
using module '.\AuditLog\Message.psm1'
using module '.\AuditLog\Event.psm1'
using module '.\AuditLog\Market.psm1'
using module '.\AuditLog\Outcome.psm1'
using module '.\AuditLog\Event\SportData.psm1'
using module '.\AuditLog\Event\Score.psm1'
using module '.\AuditLog\Message\Cardinality.psm1'
using module '.\ProcessorBase.psm1'

class LSportsProcessor: Processor {

    LSportsProcessor([SOURCE_TYPE] $sourceType): base($sourceType){
        $this.source = [SOURCE]::lsports

        $this.producers.Add('LS', 71)
        $this.mapExternalKeyPrefix.Add('lsports', '7111')

        $this.shouldReprocess = $false
        $this.isTypeBased = $false
        $this.haveMarketSeparator = $true
        $this.needFeedProducer = $false
    }

    #region Internal Resolving Methods

    hidden [string] ResolveMatchStatus([string] $state, [string] $liveCoverage){

        $_fixtureStatus = ''
        switch ($state) {
            '1' #NOT_STARTED_YET
                { $_fixtureStatus = [CONST]::STATUS_ACTIVE }
            '2' #IN_PROGRESS
                {
                    switch ($liveCoverage) {
                        'True' { $_fixtureStatus = [CONST]::STATUS_ACTIVE }
                        'False' { $_fixtureStatus = [CONST]::STATUS_SUSPENDED }
                    }
                }
            '3' #FINISHED
                { $_fixtureStatus = [CONST]::STATUS_STOPPED }
            '4' #CANCELLED
                { $_fixtureStatus = [CONST]::STATUS_SUSPENDED }
            '5' #POSTPONED
                { $_fixtureStatus = [CONST]::STATUS_SUSPENDED }
            '6' #INTERRUPTED
                { $_fixtureStatus = [CONST]::STATUS_SUSPENDED }
            '7' #ABANDONED
                { $_fixtureStatus = [CONST]::STATUS_SUSPENDED }
            '8' #COVERAGE_LOST
                {
                    switch ($liveCoverage) {
                        'True' { $_fixtureStatus = [CONST]::STATUS_ACTIVE }
                        'False' { $_fixtureStatus = [CONST]::STATUS_SUSPENDED }
                    }
                }
            '9' #ABOUT_TO_START
                { $_fixtureStatus = [CONST]::STATUS_ACTIVE }
        }

        return $_fixtureStatus
    }

    hidden [string] ResolveMatchState([string] $state){

        $_fixtureState = $state
        switch ($state) {
            '1' #NOT_STARTED_YET
                { $_fixtureState = 'Not started' }
            '2' #IN_PROGRESS
                { $_fixtureState = 'In progress' }
            '3' #FINISHED
                { $_fixtureState = 'Finished' }
            '4' #CANCELLED
                { $_fixtureState = 'Canceled' }
            '5' #POSTPONED
                { $_fixtureState = 'Postponed' }
            '6' #INTERRUPTED
                { $_fixtureState = 'Interrupted' }
            '7' #ABANDONED
                { $_fixtureState = 'Abandoned' }
            '8' #COVERAGE_LOST
                { $_fixtureState = 'Coverage lost' }
            '9' #ABOUT_TO_START
                { $_fixtureState = 'About to start' }
        }

        return $_fixtureState
    }

    hidden [string] ResolveLiveCoverage([PSCustomObject] $subscription){

        $_status = ''
        if ($subscription.Type -eq 1){ # Live
            if ($subscription.Status -eq 1){ # Subscribed
                $_status = 'True'
            }
            else {
                $_status = 'False'
            }
        }

        return $_status
    }

    hidden [string] ResolveOutcomeStatus([string] $status){

        $_outcomeStatus = $status
        switch ($status) {
            '1' #OPEN
                { $_outcomeStatus = [CONST]::STATUS_ACTIVE }
            '2' #SUSPENDED
                { $_outcomeStatus = [CONST]::STATUS_SUSPENDED }
            '3' #SETTLED
                { $_outcomeStatus = [CONST]::STATUS_RESULTED }
        }

        return $_outcomeStatus
    }

    hidden [hashtable] GetSpecifiers([string] $spec) {

        if (-not $spec) { return @{} }

        $_line = $spec.Split('/')
        $_line[0] = $_line[0].Split(' (')[0]
        if ($_line[1]) { $_line[1] = $_line[1].Split(' (')[0] }

        if ($_line[1]){
            return [ordered]@{line=$_line[0]; line2=$_line[1]}
        }
        else {
            return [ordered]@{line=$_line[0]}
        }

    }

    hidden [string] ResolveSettlement([string] $result){

        $_settlement = $result
        switch ($result) {
            '-1' #CANCELED
                { $_settlement = 'Canceled' }
            '1' #LOSER
                { $_settlement = 'Losing' }
            '2' #WINNER
                { $_settlement = 'Winning' }
            '3' #REFUND
                { $_settlement = 'Refund' }
            '4' #HALF_LOST
                { $_settlement = 'Half lost' }
            '5' #HALF_WIN
                { $_settlement = 'Half win' }
        }

        return $_settlement
    }

    #endregion

    #region Processing Methods

    [void] ProcessMessage([PSCustomObject] $line) {
        ([Processor]$this).ProcessMessage($line)

        $this.message.uniqueIdentifier = $line.content.Header.ServerTimestamp
        $this.message.timestamp = $line.content.Header.ServerTimestamp
        $this.message.producer = $line.content.Header.producerName
    }

    [void] ProcessEvent([PSCustomObject] $line, [bool] $includeSportData) {

        $this.ProcessMessage($line)

        $eventContent = $null
        switch ($line.message_type) {
            'SNAPSHOT' {
                $eventContent = $line.content.Body.Fixture
            }
            'FIXTURE_METADATA_UPDATE' {
                $eventContent = $line.content.Body.Events.Fixture
            }
        }
        if ($null -ne $eventContent){
            $this.message.event = $this.getEvent($eventContent, $includeSportData)
        }
        else {
            $this.message.event = [Event]::new()
        }

        $this.message.event.action = $line.message_type
        $this.isEventProcessed = $true
    }

    [void] ProcessCardinality([PSCustomObject] $line) {

        $this.message.cardinality = $this.getCardinality($line.content)
    }

    [void] ProcessEnrichment([PSCustomObject] $line) {
        $this.ProcessEvent($line, $false)
        $this.message.event.scoreDetails = $this.getScoreboard($line.content)
    }

    [void] ProcessMarket([PSCustomObject] $line, [Nullable[SEARCH_SCOPE]] $searchScope, [string[]] $marketId, [string[]] $outcomeId) {

        if (-not $searchScope) { $searchScope = [SEARCH_SCOPE]::internalId }
        # if someone accidentially put Singular transformed ID's
        $_marketId = @()
        foreach ($m in $marketId) {
            if ($m.Contains('_')){
                $m = $m.Split('_')[1]
            }
            $_marketId += $m
        }

        if (-not $this.message.event) {
            $this.ProcessEvent($line, $false)
        }

        $this.message.event.markets = @()
        foreach ($market in $this.FilterMarkets($line.content.Body.Events.Markets, $searchScope, $_marketId)) {
            $allMarkets = $this.getMarket($market, $outcomeId)
            foreach ($m in $allMarkets) {
                $m.action = $line.message_type
                $this.message.event.markets += $m
            }

        }
        $this.isMarketProcessed = $true
    }

    [bool] isEventRelated([PSCustomObject] $line){
        return ($line.message_type -in ('SNAPSHOT','FIXTURE_METADATA_UPDATE'))
    }

    [bool] isEnrichmentRelated([PSCustomObject] $line){
        return ($line.content.Body.Events.Livescore)
    }

    [bool] isMarketRelated([PSCustomObject] $line, [Nullable[SEARCH_SCOPE]] $searchScope, [string[]] $marketId){
        if (-not $searchScope) { $searchScope = [SEARCH_SCOPE]::internalId }
        return ($null -ne $this.FilterMarkets($line.content.Body.Events.Markets, $searchScope, $marketId))
    }

    #endregion

    #region Private Methods

    hidden [PSCustomObject] FilterMarkets([PSCustomObject] $marketsContent, [SEARCH_SCOPE] $searchScope, [string[]] $marketId){
        return ($marketsContent | Where-Object { ($searchScope -eq [SEARCH_SCOPE]::internalId -and $_.id -in $marketid) -or `
                ('*' -in $marketid) })
    }

    hidden [PSCustomObject] FilterOutcomes([PSCustomObject] $outcomesContent, [string] $line, [string[]] $outcomeId){
        return $outcomesContent | Where-Object { (($_.id -in $outcomeId) -or ('*' -in $outcomeId)) -and (-not $line -or $_.Line -eq $line) }
    }

    hidden [Event] getEvent([PSCustomObject] $messageContent, [bool] $includeSportData){
        [Event] $event = [Event]::new()

        if ($includeSportData) { $event.sportData = $this.getSportData($messageContent) }

        $event.startDate = (ConvertLogDate ([DateTimeHelper]::ConvertFromTimestamp($messageContent.StartDate, $true))  '')
        $event.liveCoverage = $this.ResolveLiveCoverage($messageContent.Subscription)
        $event.status = $this.ResolveMatchStatus($messageContent.status, $event.liveCoverage)

        $event.other = @{}
        if ($this.ResolveMatchState($messageContent.status)) { $event.info.add('state', $this.ResolveMatchState($messageContent.status)) }


        return $event
    }

    hidden [Market[]] getMarket([PSCustomObject] $marketContent, [string[]] $outcomeId) {
        $allMarkets = @()
        $allLines = ($marketContent.Bets.Line | Select-Object -Unique)

        if ($allLines.Count -gt 0){
            foreach ($line in $allLines) {
                [Market] $market = [Market]::new()

                $market.id = $marketContent.id
                $market.specifiers = $this.GetSpecifiers($line)
                $market.status = $marketContent.status

                foreach ($outcome in $this.FilterOutcomes($marketContent.Bets, $line, $outcomeId)) {
                    $market.outcomes += $this.getOutcome($outcome)
                }
                $market.other = @{}
                $allMarkets += $market
            }
        }
        else {
            [Market] $market = [Market]::new()

                $market.id = $marketContent.id
                $market.status = $marketContent.status

                foreach ($outcome in $this.FilterOutcomes($marketContent.Bets, $null, $outcomeId)) {
                    $market.outcomes += $this.getOutcome($outcome)
                }
                $market.other = @{}
                $allMarkets += $market
        }

        return $allMarkets
    }

    hidden [Outcome] getOutcome([PSCustomObject] $outcomeContent) {
        [Outcome] $outcome = [Outcome]::new()

        $outcome.id = $outcomeContent.id
        $outcome.name = $outcomeContent.Name
        $outcome.status = $this.ResolveOutcomeStatus($outcomeContent.status)
        $outcome.specifiers = $this.GetSpecifiers($outcomeContent.Line)
        $outcome.price = $outcomeContent.Price
        if ($outcomeContent.Settlement) {
            $outcome.resulted = 'True'
        }
        else {
            $outcome.resulted = ''
        }
        $outcome.settlement = $this.ResolveSettlement($outcomeContent.Settlement)
        $outcome.other = @{}

        return $outcome
    }

    hidden [SportData] getSportData([PSCustomObject] $messageContent){
        $_sportId = $messageContent.Sport.Id
        $_sport = $messageContent.Sport.Name
        $_categoryId = $messageContent.Location.Id
        $_category = $messageContent.Location.Name
        $_leagueId = $messageContent.League.Id
        $_league = $messageContent.League.Name
        $_matchId = $messageContent.FixtureId
        $_match = $messageContent.Name

        $_homeId = ''; $_home = ''; $_awayId = ''; $_away = ''
        foreach ($i in $messageContent.Participants) {
            switch ($i.Position) {
                1 {
                    $_homeId = $i.Id
                    $_home = $i.Name
                    break
                }
                2 {
                    $_awayId = $i.Id
                    $_away = $i.Name
                    break
                }
            }
        }
        $_references = ''

        return [SportData]::new($_sportId, $_sport, $_categoryId, $_category, $_leagueId, $_league, $_matchId, $_match, $_homeId, $_home, $_awayId, $_away, $_references)
    }

    hidden [Score] getScoreboard([PSCustomObject] $messageContent){
        $_home=''; $_away=''; $_currentServer=''; $_playingMinute=''

        $_periodId = $messageContent.Body.Events.Livescore.Scoreboard.CurrentPeriod
        $_playingMinute = ($messageContent.Body.Events.Livescore.Scoreboard.Time / 60)
        foreach ($i in $messageContent.Body.Events.Livescore.Scoreboard.Results) {
            switch ($i.Position) {
                1 { $_home = $i.Value }
                2 { $_away = $i.Value}
            }
        }
        $_scores = @()
        foreach ($i in $messageContent.Body.Events.Livescore.Periods) {
            $_h='NA'; $_a='NA'
            foreach ($j in $i.Results) {
                switch ($j.Position) {
                    1 { $_h = $j.Value }
                    2 { $_a = $j.Value}
                }
            }
            $_scores += $_h + ':' + $_a
        }
        foreach ($i in $messageContent.Body.Events.Livescore.LivescoreExtraData) {
            if ($i.Name -eq 'Turn'){
                switch ($i.Value) {
                    1 { $_currentServer = 'HOME' }
                    2 { $_currentServer = 'AWAY' }
                }
            }
        }
        $_data = $messageContent.Body.Events.Livescore

        return [Score]::new($_periodId, $_home, $_away, $_scores, $_currentServer, $_playingMinute, $_data)
    }

    hidden [Cardinality] getCardinality([PSCustomObject] $messageContent){
        $cardinality = [Cardinality]::new()

        $cardinality.markets = $messageContent.Body.Events.Markets.count
        $cardinality.outcomes = $messageContent.Body.Events.Markets.Bets.count


        if ($cardinality.markets -eq 0){
            if ($this.isEventRelated($messageContent)) {
                $cardinality.fixtures = 1
            }
            else {
                $cardinality.other = 1
            }
        }

        return $cardinality
    }

    #endregion
}