module Database exposing (Database, decode, get)

import Card exposing (CardData, CardDetails, CardPerformanceData, CardType(..), ManaColor(..), Power(..))
import Dict exposing (Dict)
import Json.Decode as Decode exposing (Decoder, decodeString)
import Json.Decode.Pipeline exposing (optional, required)
import Tuple exposing (pair)


type Database
    = Database (Dict String CardData)


get : String -> Database -> Maybe CardData
get name (Database db) =
    Dict.get name db


decode : String -> String -> Result String Database
decode setData draftData =
    case ( decodeString decodeSetData setData, decodeString decodePerformanceData draftData ) of
        ( Ok set, Ok draft ) ->
            Ok (createDatabase draft set |> Database)

        ( Err err, _ ) ->
            Err <| Decode.errorToString err

        ( _, Err err ) ->
            Err <| Decode.errorToString err


createDatabase : Dict String CardPerformanceData -> Dict String CardDetails -> Dict String CardData
createDatabase performanceData detailsData =
    Dict.merge
        (\_ _ cardData -> cardData)
        (\name performance details -> Dict.insert name (CardData details performance))
        (\_ _ cardData -> cardData)
        performanceData
        detailsData
        Dict.empty


decodePerformanceData : Decoder (Dict String CardPerformanceData)
decodePerformanceData =
    Decode.field "card_performance_data"
        (Decode.dict decodeCardPerformance)


decodeSetData : Decoder (Dict String CardDetails)
decodeSetData =
    Decode.list decodeCardDetails
        |> Decode.map Dict.fromList


decodeCardPerformance : Decoder CardPerformanceData
decodeCardPerformance =
    Decode.succeed CardPerformanceData
        |> required "total_times_seen" Decode.int
        |> required "total_times_picked" Decode.int
        |> required "avg_pick_position" (Decode.nullable Decode.float)
        |> required "avg_seen_position" (Decode.nullable Decode.float)
        |> required "game_in_hand_win_rate" (Decode.nullable Decode.float)


decodeCardDetails : Decoder ( String, CardDetails )
decodeCardDetails =
    Decode.map2 pair
        (Decode.field "name" Decode.string)
        (Decode.succeed CardDetails
            |> required "cmc" Decode.int
            |> required "type_line" decodeCardType
            |> required "type_line" Decode.string
            |> optional "oracle_text" (Decode.nullable Decode.string) Nothing
            |> optional "power" (Decode.nullable decodePower) Nothing
            |> optional "toughness" (Decode.nullable decodePower) Nothing
            |> required "colors" (Decode.list decodeManaColor)
        )


decodePower : Decoder Power
decodePower =
    Decode.string
        |> Decode.andThen
            (\s ->
                if s == "*" then
                    Decode.succeed VariablePower

                else
                    case String.toInt s of
                        Just i ->
                            Decode.succeed (ConstantPower i)

                        Nothing ->
                            Decode.fail "Invalid integer"
            )


decodeManaColor : Decoder ManaColor
decodeManaColor =
    Decode.string
        |> Decode.andThen
            (\c ->
                case c of
                    "R" ->
                        Decode.succeed Red

                    "U" ->
                        Decode.succeed Blue

                    "G" ->
                        Decode.succeed Green

                    "W" ->
                        Decode.succeed White

                    "B" ->
                        Decode.succeed Black

                    "C" ->
                        Decode.succeed Colorless

                    _ ->
                        Decode.fail "Invalid mana color"
            )


decodeCardType : Decoder CardType
decodeCardType =
    Decode.string
        |> Decode.map
            (\c ->
                if String.contains "Creature" c then
                    Creature

                else if String.contains "Instant" c then
                    Instant

                else if String.contains "Sorcery" c then
                    Sorcery

                else if String.contains "Enchantment" c then
                    Enchantment

                else if String.contains "Artifact" c then
                    Artifact

                else if String.contains "Planeswalker" c then
                    Planeswalker

                else if String.contains "Land" c then
                    Land

                else
                    Other
            )
