//
//  ServerSession.swift
//  forShip
//
//  Created by Mamoru Sugita on 2023/10/25.
//

import Foundation

typealias SessionResponse = Result<Data, APIError>

class ServerSession{

    /// JSON 文字列に変換
    func toJSON<T>(_ reqparam : T) -> Data! where T : Encodable {
        let Jencoder = JSONEncoder()
        var postdata : Data!
        do {
            postdata = try Jencoder.encode(reqparam)
        } catch {
            return nil
        }
        return postdata
    }

    /// JSON 文字列から変換
    func fromJSON<T>(resultData: Data, resltType : T.Type ) -> T! where T : Decodable {
        do{
            let decoder = JSONDecoder()
            let resjson : T = try decoder.decode(resltType.self, from: resultData)
            return resjson
        } catch {
            let jsonstr = String(data: resultData, encoding: .utf8)!
            print(debug: "\(jsonstr)")
            return nil
        }
    }

    func postJson(_ req_url : URL, _ postdata : Data, completion: @escaping (SessionResponse) -> Void){
        var req = URLRequest(url: req_url)
        req.httpMethod = "POST"
        req.addValue("application/json", forHTTPHeaderField: "Content-Type")
        req.httpBody = postdata
        req.timeoutInterval = 20     // タイムアウト3秒 → Amedasでタイムアウトエラー20秒に変更

        // サーバー送信
        let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)

        session.dataTask(with: req, completionHandler: {(data, response, error ) in
            session.finishTasksAndInvalidate()

            do{
                if error != nil{
//                    print(debug: "clientError: \(error)")
                    throw APIError.clientError
                }
                guard let indata = data, let inresponse = response as? HTTPURLResponse else {
                    throw APIError.responseError
                }
                if inresponse.statusCode != 200 {
//                    let statusCode = inresponse.statusCode
//                    print(debug: "serverError: \(statusCode)")
                    throw APIError.serverError
                }
                //Cookieを保存
                if let fields = inresponse.allHeaderFields as? [String: String], let url = inresponse.url {
                    for cookie in HTTPCookie.cookies(withResponseHeaderFields: fields, for: url) {
                        HTTPCookieStorage.shared.setCookie(cookie)
                    }
                }
                DispatchQueue.main.async {
                    completion(.success(indata))
                }
            }catch{
                if error as? APIError != nil{
                    completion(.failure(error as! APIError))
                }
                else{
                    completion(.failure(.unknown))
                }
            }
        }).resume()
    }

    func postForm(boundary: String, _ req_url : URL, _ postdata : Data, completion: @escaping (SessionResponse) -> Void){
        var req = URLRequest(url: req_url)
        req.httpMethod = "POST"
        req.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        req.setValue("\(postdata.count)", forHTTPHeaderField: "Content-Length")
        req.httpBody = postdata
//        req.timeoutInterval = 20     // タイムアウト3秒 → Amedasでタイムアウトエラー20秒に変更

//        let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)
        URLSession.shared.dataTask(with: req) {(data, response, error) in

//        var req = URLRequest(url: req_url)
//        req.httpMethod = "POST"
//        let headers = ["Content-Type":"multipart/form-data; boundary=\(boundary)"]
//        let urlConfig = URLSessionConfiguration.default
//        urlConfig.httpAdditionalHeaders = headers
//
//        // サーバー送信
//        let session = URLSession(configuration: urlConfig)
//        session.uploadTask(with: req, from: postdata, completionHandler: {(data, response, error) in

            do{
                if error != nil{
//                    print(debug: "clientError: \(error)")
                    completion(.failure(.unknown))
                    throw APIError.clientError
                }
                guard let indata = data, let inresponse = response as? HTTPURLResponse else {
                    completion(.failure(.unknown))
                    throw APIError.responseError
                }
                if inresponse.statusCode != 200 {
                    completion(.failure(.unknown))
//                    let statusCode = inresponse.statusCode
//                    print(debug: "serverError: \(statusCode)")
                    throw APIError.serverError
                }
                //Cookieを保存
                if let fields = inresponse.allHeaderFields as? [String: String], let url = inresponse.url {
                    for cookie in HTTPCookie.cookies(withResponseHeaderFields: fields, for: url) {
                        HTTPCookieStorage.shared.setCookie(cookie)
                    }
                }
                DispatchQueue.main.async {
                    completion(.success(indata))
                }
            }catch{
                if error as? APIError != nil{
                    completion(.failure(error as! APIError))
                }
                else{
                    completion(.failure(.unknown))
                }
            }
        }.resume()
    }

    func getJson(_ req_url : URL, completion: @escaping (SessionResponse) -> Void){

        var req = URLRequest(url: req_url)
        req.addValue("application/json", forHTTPHeaderField: "Content-Type")
        req.timeoutInterval = 20     // タイムアウト3秒 → Amedasでタイムアウトエラー20秒に変更

        // サーバー送信
        let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)

        session.dataTask(with: req, completionHandler: {(data, response, error ) in
            session.finishTasksAndInvalidate()

            do{
                if error != nil{
//                    print(debug: "clientError: \(error)")
                    completion(.failure(.unknown))
                    throw APIError.clientError
                }

                guard let indata = data, let inresponse = response as? HTTPURLResponse else {
                    completion(.failure(.unknown))
                    throw APIError.responseError
                }
                if inresponse.statusCode != 200 {
                    completion(.failure(.unknown))
//                    let statusCode = inresponse.statusCode
//                    print(debug: "serverError: \(statusCode)")
                    throw APIError.serverError
                }
                DispatchQueue.main.async {
                    completion(.success(indata))
                }
            }catch{
                if error as? APIError != nil{
                    completion(.failure(error as! APIError))
                }
                else{
                    completion(.failure(.unknown))
                }
            }
        }).resume()
    }

    func deleteJson(_ req_url : URL, completion: @escaping (SessionResponse) -> Void){

        var req = URLRequest(url: req_url)
        req.httpMethod = "DELETE"
        req.addValue("application/json", forHTTPHeaderField: "Content-Type")
        req.timeoutInterval = 20     // タイムアウト3秒 → Amedasでタイムアウトエラー20秒に変更

        // サーバー送信
        let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)

        session.dataTask(with: req, completionHandler: {(data, response, error ) in
            session.finishTasksAndInvalidate()

            do{
                if error != nil{
                    _ = APIError.clientError
                    throw APIError.clientError
                }
                guard let indata = data, let inresponse = response as? HTTPURLResponse else {
                    throw APIError.responseError
                }
                if inresponse.statusCode != 200 {
                    throw APIError.serverError
                }
                DispatchQueue.main.async {
                    completion(.success(indata))
                }
            }catch{
                if error as? APIError != nil{
                    completion(.failure(error as! APIError))
                }
                else{
                    completion(.failure(.unknown))
                }
            }
        }).resume()
    }

    // HTTPCookieStorageに特定のCookieが保存されているかを調べる
    func isCookieSet(name: String, url: URL) -> Bool {
        if let cookies = HTTPCookieStorage.shared.cookies(for: url) {
            for cookie in cookies {
                if (cookie.name == name) {
                    return true
                }
            }
        }
        return false
    }

    func httpBody(boundary: String, _ uploadImage : ReqUploadImage) -> Data! {
        var httpBody1 = "--\(boundary)\r\n"
        httpBody1 += "Content-Disposition: form-data; name=\"ShipId\"\r\n"
        httpBody1 += "\r\n"
        httpBody1 += "\(uploadImage.shipId)\r\n"
        httpBody1 += "--\(boundary)\r\n"
        httpBody1 += "Content-Disposition: form-data; name=\"MessageId\"\r\n"
        httpBody1 += "\r\n"
        httpBody1 += "\(uploadImage.messageId)\r\n"
        httpBody1 += "--\(boundary)\r\n"
        httpBody1 += "Content-Disposition: form-data; name=\"Location\"\r\n"
        httpBody1 += "\r\n"
        httpBody1 += "\(uploadImage.location)\r\n"
        httpBody1 += "--\(boundary)\r\n"
        httpBody1 += "Content-Disposition: form-data; name=\"From\"\r\n"
        httpBody1 += "\r\n"
        httpBody1 += "\(uploadImage.from)\r\n"
        httpBody1 += "--\(boundary)\r\n"
        httpBody1 += "Content-Disposition: form-data; name=\"FromId\"\r\n"
        httpBody1 += "\r\n"
        httpBody1 += "\(uploadImage.fromId)\r\n"
        httpBody1 += "--\(boundary)\r\n"
        httpBody1 += "Content-Type: image/jpeg\r\n"
        httpBody1 += "\r\n"

        var httpBody = Data()
        httpBody.append(httpBody1.data(using: .utf8)!)
        httpBody.append(uploadImage.files)

        var httpBody2 = "\r\n"
        httpBody2 += "--\(boundary)--\r\n"

        httpBody.append(httpBody2.data(using: .utf8)!)

        return httpBody
    }
}
