//
//  MessageAPI.swift
//  CovidSafe
//
//  Copyright © 2020 Australian Government. All rights reserved.
//

import Foundation
import Alamofire

class StatisticsAPI: CovidSafeAuthenticatedAPI {
    
    static let keyCovidStatistics = "keyCovidStatistics"
    
    static func getStatistics(forState: StateTerritory = StateTerritory.AU, completion: @escaping (StatisticsResponse?, CovidSafeAPIError?) -> Void) {
        guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
            completion(nil, .RequestError)
            return
        }
        
        let parameters = ["state" : "\(forState.rawValue)"]
        
        guard let authHeaders = try? authenticatedHeaders() else {
            completion(nil, .RequestError)
            return
        }
        
        CovidNetworking.shared.session.request("\(apiHost)/v2/statistics",
            method: .get,
            parameters: parameters,
            headers: authHeaders,
            interceptor: CovidRequestRetrier(retries: 3)
        ).validate().responseDecodable(of: StatisticsResponse.self) { (response) in
            switch response.result {
            case .success:
                guard let statisticsResponse = response.value else { return }
                let statsData = try? PropertyListEncoder().encode(statisticsResponse)
                UserDefaults.standard.set(statsData, forKey: keyCovidStatistics)
                
                completion(statisticsResponse, nil)
            case .failure(_):
                var lastStats: StatisticsResponse? = nil
                if let savedStats = UserDefaults.standard.data(forKey: keyCovidStatistics) {
                    lastStats = try? PropertyListDecoder().decode(StatisticsResponse.self, from: savedStats)
                }
                guard let statusCode = response.response?.statusCode else {
                    completion(lastStats, .UnknownError)
                    return
                }
                if (statusCode == 200) {
                    completion(lastStats, .ResponseError)
                    return
                }
                
                if statusCode == 401, let respData = response.data {
                    completion(nil, processUnauthorizedError(respData))
                    return
                }
                
                if (statusCode >= 400 && statusCode < 500) {
                    completion(lastStats, .RequestError)
                    return
                }
                completion(lastStats, .ServerError)
            }
        }
    }
}

struct StatisticsResponse: Codable {
    
    let updatedDate: String?
    let responseVersion: String?
    
    let national: StateTerritoryStatistics?
    let act: StateTerritoryStatistics?
    let nsw: StateTerritoryStatistics?
    let nt: StateTerritoryStatistics?
    let qld: StateTerritoryStatistics?
    let sa: StateTerritoryStatistics?
    let tas: StateTerritoryStatistics?
    let vic: StateTerritoryStatistics?
    let wa: StateTerritoryStatistics?
    
    enum CodingKeys: String, CodingKey {
        case updatedDate = "updated_date"
        case national
        case act
        case nsw
        case nt
        case qld
        case sa
        case tas
        case vic
        case wa
        case responseVersion = "version"
    }
    
    func version() -> Int {
        guard let versionStr = responseVersion else {
            return 0
        }
        return Int(versionStr) ?? 0
    }
}

struct StateTerritoryStatistics: Codable {
    let totalCases: Int?
    let activeCases: Int?
    let newCases: Int?
    let recoveredCases: Int?
    let deaths: Int?
    let newLocallyAcquired: Int?
    let locallyAcquired: Int?
    let newOverseasAcquired: Int?
    let newUnderInvestigation: Int?
    let overseasAcquired: Int?
    let historicalCases: [HistoricalCase]?
    
    enum CodingKeys: String, CodingKey {
        case totalCases = "total_cases"
        case activeCases = "active_cases"
        case newCases = "new_cases"
        case recoveredCases = "recovered_cases"
        case newLocallyAcquired = "new_locally_acquired"
        case locallyAcquired = "locally_acquired"
        case newOverseasAcquired = "new_overseas_acquired"
        case overseasAcquired = "overseas_acquired"
        case historicalCases = "historical_cases"
        case newUnderInvestigation = "new_under_investigation"
        case deaths
    }
}

struct HistoricalCase: Codable {
    let date: String
    let newCases: Int
    
    enum CodingKeys: String, CodingKey {
        case date
        case newCases = "new_cases"
    }
}
