//
//  SessionUploadImage.swift
//  Sailassist
//
//  Created by 三浦薫巳 on 2023/11/23.
//

import Foundation
import SwiftUI

class SessionUploadImage : ObservableObject {
    @Published var progress: Double = 0.0
    @Published var isUploading: Bool = false
    static let OnlyOne = SessionUploadImage()   // シングルトン宣言

    private var serverSession = ServerSession()
    private var Calling : Bool = false      // 通信中

    private var uploadTask: URLSessionUploadTask?

    func cancelUpload() {
        uploadTask?.cancel()
        uploadTask = nil
        isUploading = false
        progress = 0.0
    }

    //MARK: - 画像のアップロード
    func requestUploadImage(_ uploadImage: ReqUploadImage) async throws -> Data {
        print(debug: "called")
        guard !Calling else { throw APIError.busy }
        Calling = true
        defer { Calling = false }

        guard let req_url = URL(string: HttpRequestType.UploadImage.rawValue) else {
            throw APIError.invalidURL
        }

        let boundary = "----------\(UUID().uuidString)"
        var httpBody = Data()
        func appendFormField(name: String, value: String) {
            httpBody.append("--\(boundary)\r\n".data(using: .utf8)!)
            httpBody.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n".data(using: .utf8)!)
            httpBody.append("\(value)\r\n".data(using: .utf8)!)
        }

        appendFormField(name: "ShipId", value: String(uploadImage.shipId))
        appendFormField(name: "MessageId", value: uploadImage.messageId)
        appendFormField(name: "Location", value: String(uploadImage.location))
        appendFormField(name: "From", value: uploadImage.from)
        appendFormField(name: "FromId", value: uploadImage.fromId)

        httpBody.append("--\(boundary)\r\n".data(using: .utf8)!)
        httpBody.append("Content-Disposition: form-data; name=\"files\"; filename=\"itemp.jpg\"\r\n".data(using: .utf8)!)
        httpBody.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
        httpBody.append(uploadImage.files)
        httpBody.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

        var request = URLRequest(url: req_url)
        request.httpMethod = "POST"
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        let session = URLSession(configuration: .default, delegate: UploadProgressDelegate(parent: self), delegateQueue: nil)

        isUploading = true

        return try await withCheckedThrowingContinuation { continuation in
            self.uploadTask = session.uploadTask(with: request, from: httpBody) { data, response, error in
                DispatchQueue.main.async {
                    self.isUploading = false
                    self.progress = 0.0
                }

                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }

                guard let httpResponse = response as? HTTPURLResponse,
                      (200...299).contains(httpResponse.statusCode),
                      let data = data else {
                    continuation.resume(throwing: APIError.serverError)
                    return
                }

                continuation.resume(returning: data)
            }

            self.uploadTask?.resume()
        }
    }

    // MARK: - 動画のアップロード
    func requestUploadVideo(_ uploadVideo: ReqUploadImage) async throws -> Data {
        print("Video upload called")
        guard !Calling else {
            throw APIError.busy
        }
        Calling = true
        defer { Calling = false }

        guard let req_url = URL(string: HttpRequestType.UploadImage.rawValue) else {
            throw APIError.invalidURL
        }

        let boundary = "----------\(UUID().uuidString)"
        var httpBody = Data()

        func appendFormField(name: String, value: String) {
            httpBody.append("--\(boundary)\r\n".data(using: .utf8)!)
            httpBody.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n".data(using: .utf8)!)
            httpBody.append("\(value)\r\n".data(using: .utf8)!)
        }

        appendFormField(name: "ShipId", value: String(uploadVideo.shipId))
        appendFormField(name: "MessageId", value: uploadVideo.messageId)
        appendFormField(name: "Location", value: String(uploadVideo.location))
        appendFormField(name: "From", value: uploadVideo.from)
        appendFormField(name: "FromId", value: uploadVideo.fromId)

        httpBody.append("--\(boundary)\r\n".data(using: .utf8)!)
        httpBody.append("Content-Disposition: form-data; name=\"files\"; filename=\"itemp.mp4\"\r\n".data(using: .utf8)!)
        httpBody.append("Content-Type: video/mp4\r\n\r\n".data(using: .utf8)!)
        httpBody.append(uploadVideo.files)
        httpBody.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

        var request = URLRequest(url: req_url)
        request.httpMethod = "POST"
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        let session = URLSession(configuration: .default, delegate: UploadProgressDelegate(parent: self), delegateQueue: nil)

        isUploading = true

        return try await withCheckedThrowingContinuation { continuation in
            self.uploadTask = session.uploadTask(with: request, from: httpBody) { data, response, error in
                DispatchQueue.main.async {
                    self.isUploading = false
                    self.progress = 0.0
                }

                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }

                guard let httpResponse = response as? HTTPURLResponse,
                      (200...299).contains(httpResponse.statusCode),
                      let data = data else {
                    continuation.resume(throwing: APIError.serverError)
                    return
                }

                continuation.resume(returning: data)
            }

            self.uploadTask?.resume()
        }
    }

    func postFormAsync(boundary: String, url: URL, body: Data) async throws -> Data {
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        request.httpBody = body

        let (data, response) = try await URLSession.shared.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
            throw APIError.serverError
        }

        return data
    }

    class UploadProgressDelegate: NSObject, URLSessionTaskDelegate {
        weak var parent: SessionUploadImage?

        init(parent: SessionUploadImage) {
            self.parent = parent
        }

        func urlSession(_ session: URLSession, task: URLSessionTask,
                        didSendBodyData bytesSent: Int64,
                        totalBytesSent: Int64,
                        totalBytesExpectedToSend: Int64) {
            guard totalBytesExpectedToSend > 0 else { return }
            DispatchQueue.main.async {
                self.parent?.progress = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
            }
        }
    }
}
