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

import Combine

import KakaoSDKCommon
import KakaoSDKAuth
import KakaoSDKUser

import Foundation

class LoginController {
    private var cancellables = Set<AnyCancellable>()
    
    func login(_ type: SNSLoginType, _ id: String = "") -> AnyPublisher<SNSID,Error> {
        switch type {
        case .Kakao:
            return self.checkKakaoToken()
                .handleEvents(receiveCompletion:  { completion in
                    switch completion {
                    case .failure(let error) :
                        printLog("KAKAO LOGIN ERROR: \(error)")
                    case .finished: break
                    }
                })
                .eraseToAnyPublisher()
            
        case .Apple:
            return Fail(error: NSError(domain: "Apple login not implemented", code: 1, userInfo: nil))
                .eraseToAnyPublisher()
        case .Dev:
            return Future<SNSID, Error> { promise in
                var snsId = SNSID()
                snsId.acctType = "dev"
                snsId.snsId = "\(id)"
                snsId.snsToken = "devToken"
                promise(.success(snsId))
            }
            .eraseToAnyPublisher()
        }
        
        
    }
    
    func logout(type: SNSLoginType) {
        switch type {
        case .Kakao:
            UserApi.shared.logout { error in
                if let error = error {
                    printLog(error)
                }
                else {
                    printLog("LOGOUT SUCCESS")
                }
            }
        case .Apple: break
        case .Dev: break
        }
    }
}


//MARK: - KAKAO LOGIN
extension LoginController {
    /// 토큰을 가지고 있나 확인
    private func checkKakaoToken() -> Future<SNSID, Error> {
        return Future { promise in
            // 토큰 확인
            if AuthApi.hasToken() {
                printLog("토큰 있음")
                self.analysisKakaoToken()
                    .flatMap{ token in
                        self.generateSNSID(token)
                    }
                    .sink { completion in
                        switch completion {
                        case .failure(let error):
                            promise(.failure(error))
                        case .finished: break
                        }
                    } receiveValue: { data in
                        promise(.success(data))
                    }
                    .store(in: &self.cancellables)
            }
            else {
                // 처음 설치하거나 하면 여기로 들어오게 됨 -> 당연히 없으니까 바로
                printLog("토큰 없음")
                self.loginKakao()
                    .flatMap { token in
                        self.generateSNSID(token)
                    }
                    .sink { completion in
                        switch completion {
                        case .failure(let error):
                            promise(.failure(error))
                        case .finished: break
                        }
                    } receiveValue: { data in
                        promise(.success(data))
                    }
                    .store(in: &self.cancellables)
                
            }
        }
    }
    
    private func generateSNSID(_ token: String) -> Future<SNSID, Error> {
        return Future { promise in
            var snsId = SNSID()
            UserApi.shared.me { user, error in
                if let error = error {
                    promise(.failure(error))
                }
                else if let user = user, let id = user.id{
                    snsId.acctType = "kakao"
                    snsId.snsId = "\(id)"
                    snsId.snsToken = "\(token)"
                    if let email = user.kakaoAccount?.email {
                        snsId.snsEmail = email
                    }
                    promise(.success(snsId))
                }
            }
        }
    }
    
    
    
    /// access 토큰의 유효성을 검증
    private func analysisKakaoToken() -> Future<String, Error> {
        return Future { promise in
            UserApi.shared.accessTokenInfo { tokenInfo, error in
                if let error = error {
                    printLog("토큰 유효성 체크 실패 - 로그인 동작 필요")
                    if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true {
                        // 로그인이 필요
                        self.loginKakao()
                        // 로그인 후 동작이 sink에서 처리 될것
                            .sink { completion in
                                switch completion {
                                case .failure(let error):
                                    promise(.failure(error))
                                case .finished: break
                                    // 로그 인 후 추가 동작 할 거 있나? 일단 토큰 받아서 만들어 보내는건 Value에서 함
                                }
                            } receiveValue: { token in
                                printLog("로그인 완료 - 토큰 받아옴 : \(token)")
                                promise(.success(token))
                            }
                            .store(in: &self.cancellables)
                    }
                    else {
                        // 그 외 기타 에러
                        promise(.failure(error))
                    }
                }
                else {
                    printLog("토큰 유효성 체크 성공")
                    // 토큰 유효성이 체크 성공, 로그인 불필요 - 해당 토큰으로 카카오 API 호출 가능
                    if let tokenInfo = tokenInfo,
                       let token = Auth.shared.tokenManager.getToken()?.accessToken {
                        promise(.success(token))
                    }
                }
            }
        }
    }
    
    private func loginKakao() -> Future<String, Error> {
        return Future { promise in
            if (UserApi.isKakaoTalkLoginAvailable()) { // 카카오톡 앱 실행 가능
                UserApi.shared.loginWithKakaoTalk { oauthToken, error in
                    if let error = error { // 로그인과정에서 오류 발생
                        promise(.failure(error))
                    }
                    else if let oauthToken = oauthToken { // 정상 로그인 완료
                        promise(.success(oauthToken.accessToken))
                    }
                }
            }
            else { // 카카오톡 앱 실행 불가
                UserApi.shared.loginWithKakaoAccount { oauthToken, error in
                    if let error = error { // 로그인과정에서 오류 발생
                        promise(.failure(error))
                    }
                    else if let oauthToken = oauthToken { // 정상 로그인 완료
                        promise(.success(oauthToken.accessToken))
                    }
                }
            }
        }
    }
}
