//
//  APIController.swift
//  AcaMate
//
//  Created by Sean Kim on 11/26/24.
//


import Foundation
import Combine
import Alamofire

public class APIManager {
    private var cancellables = Set<AnyCancellable>()
//    public static let shared = APIManager()
    
    @UserDefault(key: "refresh", defaultValue: "refreshToken") var refresh
    @UserDefault(key: "token", defaultValue: "accToken") var accToken
    
//    private init(cancellables: Set<AnyCancellable> = Set<AnyCancellable>()) {
//        self.cancellables = cancellables
//    }
    
    public func loadAPIData<T: APIResponseProtocol>(_ request: APIRequest<T>) -> Future<T, Error> {
        let encoding: ParameterEncoding = (request.method == .get) ? URLEncoding.default : JSONEncoding.default
        
        return Future { promise in
            printLog(request.parameters)
            AF.request("\(request.url)\(request.path)",
                       method: request.method,
                       parameters: request.parameters,
                       encoding: encoding,
                       headers: request.headers
            )
            .validate(statusCode: 200 ..< 300)
            .responseDecodable(of: request.decoding) { response in
                
                switch response.result {
                case .success(let value):
                    printLog("Good: \(value)")
                    promise(.success(value))
                    
                case .failure(let error):
                    printLog("Bad: \(error))")
                    promise(.failure(error))
                    
                }
            }
        }
    }
    
    
    public func reloadAccessToken() -> Future<Any, Error> {
        return Future { [weak self] promise in
            guard let self = self else {return}
            
            let request = APIRequest.init(path: "/api/v1/in/app/retryAccess",
                                          parameters: ["refresh": refresh],
                                          decoding: APIResponse<Access>.self)
            
            APIManager().loadAPIData(request)
                .sink { completion in
                    switch completion {
                    case .failure(let error):
                        promise(.failure(error))
                        printLog("\(error)")
                    case .finished:
                        printLog("엑세스 토큰 재발급 완료")
                        break
                    }
                } receiveValue: { response in
                    guard let accToken = response as? APIResponse<Access> else {
                        promise(.failure(ACA_ERROR("Unknown ERROR")))
                        return
                    }
                    
                    switch accToken.status.code {
                    case .success(let code):
                        if code == "000" {
                            if let tknData = accToken.data { promise(.success(tknData)) }
                        }
                    case .inputErr(let code):
                        // 로그인 화면으로 전환하기
                        promise(.failure(ACA_ERROR("Refresh token ERROR(\(code), \(accToken.status.message)")))
                    default:
                        // 그외에 서버에서 처리를 하다가 문제가 생겨서 발생하는 에러는 여기로 보낼거임
                        promise(.failure(ACA_ERROR("Unknown ERROR")))
                    }
                }
                .store(in: &self.cancellables)
        }
    }
    
    
    public func loadUserAPIData<T: APIResponseProtocol & Codable>(request: APIRequest<T>) -> Future<T, Error>{
        return Future { [weak self] promise in
            guard let self = self else {return}
            printLog(request.parameters["token"])
            loadAPIData(request)
                .flatMap { (response: T) -> AnyPublisher<T, Error> in
                    switch response.status.code {
                    case .inputErr(let code) where code == "101":
                        return self.reloadAccessToken()
                            .flatMap { response -> AnyPublisher<T, Error> in
                                self.accToken = (response as! Access).access
                                var updateRequest = request
                                updateRequest.parameters["token"] = self.accToken
                                printLog("Acc: \(self.accToken)")
                                return APIManager().loadAPIData(updateRequest)
                                    .eraseToAnyPublisher()
                            }
                            .eraseToAnyPublisher()
                    default:
                        return Just(response)
                            .setFailureType(to: Error.self)
                            .eraseToAnyPublisher()
                    }
                }
                .sink { completion in
                    switch completion {
                    case .failure(let error):
                        promise(.failure(error))
                    case .finished:
                        break
                    }
                } receiveValue: { finalResponse in
                    promise(.success(finalResponse))
                }
                .store(in: &self.cancellables)
        }
    }
}


