//
//  WdtWeatherSource.swift
//  1Weather
//
//  Created by Demid Merzlyakov on 25.02.2021.
//

import Foundation
import CoreLocation
import XMLCoder

public enum WdtWeatherSourceError: Error {
    case badUrl
    case networkError(Error?)
    case badServerResponse(Error?)
    case dataMergeError(String)
}

public class WdtWeatherSource: WeatherSource {
    private let log = Logger(componentName: "WdtWeatherSource")
    private static let updateUrlMega = "https://1weather.onelouder.com/feeds/onelouder/mega.php"
    private static let updateUrlMicro = "https://1weather.onelouder.com/feeds/onelouder2/fm.php"
    
    public func updateWeather(for location: Location, completion: @escaping WeatherSourceCompletion) {
        log.debug("Start update.")
        guard var urlComponents = URLComponents(string: WdtWeatherSource.updateUrlMega) else {
            assertionFailure("Should never happen. The URL should be correct.")
            return
        }
        
        var queryParameters = [String: String]()
        
        if let coordinates = location.coordinates {
            queryParameters["LAT"] = String(format: "%.5f", coordinates.latitude)
            queryParameters["LON"] = String(format: "%.5f", coordinates.longitude)
        }
        else {
            queryParameters["ZIP"] = location.zip
            queryParameters["CITY"] = location.cityName
            queryParameters["STATE"] = location.region
            queryParameters["COUNTRY"] = location.countryName
        }
        queryParameters["UNITS"] = "all"
        urlComponents.queryItems = queryParameters.map { URLQueryItem(name: $0, value: $1) }
        
        guard let url = urlComponents.url else {
            completion(nil, WdtWeatherSourceError.badUrl)
            return
        }
        
        let urlSession = URLSession.shared
        let dataTask = urlSession.dataTask(with: url) { [weak self] (data, reponse, error) in
            guard let self = self else { return }
            guard let data = data else {
                completion(nil, WdtWeatherSourceError.networkError(error))
                return
            }
            let decoder = XMLDecoder()
            do {
                let locationResponse = try decoder.decode(WdtLocationResponse.self, from: data)
                guard let newLocation = try self.update(location: location, from: locationResponse) else {
                    completion(nil, WdtWeatherSourceError.badServerResponse(error))
                    return
                }
                
                completion(newLocation, nil)
            }
            catch {
                completion(nil, WdtWeatherSourceError.badServerResponse(error))
            }
        }
        dataTask.resume()
    }
    
    func update(location: Location, from locationResponse: WdtLocationResponse) throws -> Location? {
        guard let wdtLocation = locationResponse.locations.first else {
            return nil
        }
        let updateTime = Date()
        let incrementalChanges = try wdtLocation.toAppModel(timeZone: location.timeZone, updatedAt: updateTime)
        let updatedLocation = location.mergedWith(incrementalChanges: incrementalChanges)
        return updatedLocation
    }
}
