module Data.Search exposing (..)

import Data exposing (Id)
import Json.Decode as JD exposing (Decoder)
import Json.Decode.Pipeline exposing (required, optional)

type alias SearchResponse =
    { query: String
    , uiId: String -- we require this field because we will always set this in the request
    , isLast: Bool
    , order: Int
    , elapsedSeconds: Float
    , sorting: SortConfig
    , result: List SearchResult
    }

decodeSearchResponse : Decoder SearchResponse
decodeSearchResponse =
    JD.succeed SearchResponse
    |> required "query" JD.string
    |> required "uiId" JD.string
    |> required "isLast" JD.bool
    |> required "order" JD.int
    |> required "elapsed-seconds" JD.float
    |> required "sorting" decodeSortConfig
    |> required "result" (JD.list decodeSearchResult)

type alias SearchResult =
    { id: Id
    , database: String
    , type_: SearchResultType
    , role: Maybe String
    , title: String
    , sorting: SortInfo
    , info: List SearchInfo
    , found: List SearchFound
    }

decodeSearchResult : Decoder SearchResult
decodeSearchResult =
    JD.succeed SearchResult
    |> required "id" Data.decodeId
    |> required "database" JD.string
    |> required "type" decodeSearchResultType
    |> optional "role" (JD.nullable JD.string) Nothing
    |> required "title" JD.string
    |> required "sorting" decodeSortInfo
    |> required "info" (JD.list decodeSearchInfo)
    |> required "found" (JD.list decodeSearchFound)

type SearchResultType
    = Interview
    | Person
    | File

decodeSearchResultType : Decoder SearchResultType
decodeSearchResultType =
    JD.andThen
        (\name ->
            case name of
               "Interview" -> JD.succeed Interview
               "Person" -> JD.succeed Person
               "File" -> JD.succeed File
               _ -> JD.fail <| "Unknown type: " ++ name
        )
    <| JD.string

type alias SearchInfo =
    { name: String
    , value: String
    }

decodeSearchInfo : Decoder SearchInfo
decodeSearchInfo =
    JD.succeed SearchInfo
    |> required "name" JD.string
    |> required "value" JD.string

type alias SearchFound =
    { field: String
    , type_: SearchFoundType
    , value: List SearchValue
    }

decodeSearchFound : Decoder SearchFound
decodeSearchFound =
    JD.succeed SearchFound
    |> required "field" JD.string
    |> required "type" decodeSearchFoundType
    |> required "value" (JD.list decodeSearchValue)

type SearchFoundType
    = Field
    | Attribute

decodeSearchFoundType : Decoder SearchFoundType
decodeSearchFoundType =
    JD.andThen
        (\name ->
            case name of
                "Field" -> JD.succeed Field
                "Attribute" -> JD.succeed Attribute
                _ -> JD.fail <| "Unknown type: " ++ name
        )
        JD.string

type SearchValue
    = Ellipsis
    | Context String
    | Term String

decodeSearchValue : Decoder SearchValue
decodeSearchValue =
    JD.andThen
        (\name ->
            case name of
                "Ellipsis" -> JD.succeed Ellipsis
                "Context" ->
                    JD.succeed Context
                    |> required "value" JD.string
                "Term" ->
                    JD.succeed Term
                    |> required "value" JD.string
                _ -> JD.fail <| "Unknown type: " ++ name
        )
    <| JD.field "type" JD.string

type SortTarget
    = SortTargetNone
    | SortTargetScore
    | SortTargetAttribute
    | SortTargetField

decodeSortTarget : Decoder SortTarget
decodeSortTarget =
    JD.andThen
        (\target ->
            case target of
                "None" -> JD.succeed SortTargetNone
                "Score" -> JD.succeed SortTargetScore
                "Attribute" -> JD.succeed SortTargetAttribute
                "Field" -> JD.succeed SortTargetField
                _ -> JD.fail <| "Unknown type: " ++ target
        )
    <| JD.string

type SortDirection
    = Ascending
    | Descending

decodeSortDirection : Decoder SortDirection
decodeSortDirection =
    JD.andThen
        (\dir ->
            case dir of
                "Ascending" -> JD.succeed Ascending
                "Descending" -> JD.succeed Descending
                _ -> JD.fail <| "Unknown direction: " ++ dir
        )
    <| JD.string

type alias SortOption =
    { target: SortTarget
    , name: Maybe String
    , direction: SortDirection
    }

decodeSortOption : Decoder SortOption
decodeSortOption =
    JD.succeed SortOption
    |> required "target" decodeSortTarget
    |> required "name" (JD.nullable JD.string)
    |> required "direction" decodeSortDirection

type alias SortConfig = List SortOption

decodeSortConfig : Decoder SortConfig
decodeSortConfig = JD.list decodeSortOption

type SortInfoEntry
    = SortInfoNull
    | SortInfoString String
    | SortInfoDouble Float

decodeSortInfoEntry : Decoder SortInfoEntry
decodeSortInfoEntry =
    JD.oneOf
        [ JD.null SortInfoNull
        , JD.map SortInfoString JD.string
        , JD.map SortInfoDouble JD.float
        ]

type alias SortInfo = List SortInfoEntry

decodeSortInfo : Decoder SortInfo
decodeSortInfo = JD.list decodeSortInfoEntry
