//
//  ProfileUpdateViewModel.swift
//  SodaLive
//
//  Created by klaus on 2023/08/19.
//

import UIKit
import Combine

import Moya

final class ProfileUpdateViewModel: ObservableObject {
    
    private let repository = UserRepository()
    private var subscription = Set<AnyCancellable>()
    
    @Published var isLoading = false
    @Published var errorMessage = ""
    @Published var isShowPopup = false
    
    @Published var email = ""
    @Published var nickname = ""
    @Published var youtubeUrl = ""
    @Published var instagramUrl = ""
    @Published var websiteUrl = ""
    @Published var blogUrl = ""
    @Published var gender: Gender = .NONE
    @Published var introduce = ""
    
    @Published var currentPassword = ""
    @Published var newPassword = ""
    @Published var newPasswordConfirm = ""
    
    @Published var tags = [String]()
    @Published var insertTags = [String]()
    @Published var removeTags = [String]()
    
    var profileImage: UIImage? = nil {
        didSet {
            if let _ = profileImage {
                updateProfileImage()
            }
        }
    }
    
    var profileResponse: GetProfileResponse?
    
    var refresh: () -> Void = {}
    
    let placeholder = "소개글을 입력하세요"
    
    func getMyProfile() {
        isLoading = true
        repository.getMyProfile()
            .sink { result in
                switch result {
                case .finished:
                    DEBUG_LOG("finish")
                case .failure(let error):
                    ERROR_LOG(error.localizedDescription)
                }
            } receiveValue: { [unowned self] response in
                self.isLoading = false
                let responseData = response.data
                
                do {
                    let jsonDecoder = JSONDecoder()
                    let decoded = try jsonDecoder.decode(ApiResponse<GetProfileResponse>.self, from: responseData)
                    
                    if let data = decoded.data, decoded.success {
                        self.profileResponse = data
                        
                        self.email = data.email
                        self.nickname = data.nickname
                        self.youtubeUrl = data.youtubeUrl ?? ""
                        self.instagramUrl = data.instagramUrl ?? ""
                        self.blogUrl = data.blogUrl ?? ""
                        self.websiteUrl = data.websiteUrl ?? ""
                        self.introduce = data.introduce
                        self.gender = data.gender
                        self.tags.append(contentsOf: data.tags)
                        
                        UserDefaults.set(data.nickname, forKey: .nickname)
                    } else {
                        if let message = decoded.message {
                            self.errorMessage = message
                        } else {
                            self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        }
                        
                        self.isShowPopup = true
                    }
                } catch {
                    self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                    self.isShowPopup = true
                }
            }
            .store(in: &subscription)
    }
    
    func updateProfile() {
        if profileResponse!.nickname != nickname ||
            profileResponse!.youtubeUrl != youtubeUrl ||
            profileResponse!.instagramUrl != instagramUrl ||
            profileResponse!.blogUrl != blogUrl ||
            profileResponse!.websiteUrl != websiteUrl ||
            profileResponse!.gender != gender ||
            profileResponse!.introduce != introduce ||
            !insertTags.isEmpty ||
            !removeTags.isEmpty {
            
            let request = ProfileUpdateRequest(
                email: profileResponse!.email,
                nickname: profileResponse!.nickname != nickname ? nickname : nil,
                gender: profileResponse!.gender != gender ? gender : nil,
                introduce: profileResponse!.introduce != introduce && introduce.trimmingCharacters(in: .whitespacesAndNewlines) != placeholder ? introduce : nil,
                youtubeUrl: profileResponse!.youtubeUrl != youtubeUrl ? youtubeUrl : nil,
                instagramUrl: profileResponse!.instagramUrl != instagramUrl ? instagramUrl : nil,
                websiteUrl: profileResponse!.websiteUrl != websiteUrl ? websiteUrl : nil,
                blogUrl: profileResponse!.blogUrl != blogUrl ? blogUrl : nil,
                insertTags: !insertTags.isEmpty ? insertTags : nil,
                removeTags: !removeTags.isEmpty ? removeTags : nil
            )
            
            isLoading = true
            repository.profileUpdate(request: request)
                .sink { result in
                    switch result {
                    case .finished:
                        DEBUG_LOG("finish")
                    case .failure(let error):
                        ERROR_LOG(error.localizedDescription)
                    }
                } receiveValue: { [unowned self] response in
                    self.isLoading = false
                    let responseData = response.data
                    
                    do {
                        let jsonDecoder = JSONDecoder()
                        let decoded = try jsonDecoder.decode(ApiResponse<GetProfileResponse>.self, from: responseData)
                        
                        if let _ = decoded.data, decoded.success {
                            self.refresh()
                            self.errorMessage = "프로필이 변경되었습니다."
                            self.isShowPopup = true
                            
                            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                                AppState.shared.back()
                            }
                        } else {
                            if let message = decoded.message {
                                self.errorMessage = message
                            } else {
                                self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                            }
                            
                            self.isShowPopup = true
                        }
                    } catch {
                        self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        self.isShowPopup = true
                    }
                }
                .store(in: &subscription)
            
        } else {
            AppState.shared.back()
        }
    }
    
    func updatePassword() {
        if currentPassword.trimmingCharacters(in: .whitespaces).isEmpty {
            errorMessage = "현재 비밀번호를 입력하세요."
            isShowPopup = true
            return
        }
        
        if newPassword.trimmingCharacters(in: .whitespaces).isEmpty {
            errorMessage = "변경할 비밀번호를 입력하세요."
            isShowPopup = true
            return
        }
        
        if newPassword != newPasswordConfirm {
            errorMessage = "비밀번호가 일치하지 않습니다."
            isShowPopup = true
            return
        }
        
        if !validatePassword() {
            errorMessage = "영문, 숫자 포함 8자 이상의 비밀번호를 입력해 주세요."
            isShowPopup = true
            return
        }
        
        let request = ProfileUpdateRequest(
            email: UserDefaults.string(forKey: .email),
            password: currentPassword,
            modifyPassword: newPassword
        )
        
        isLoading = true
        repository.profileUpdate(request: request)
            .sink { result in
                switch result {
                case .finished:
                    DEBUG_LOG("finish")
                case .failure(let error):
                    ERROR_LOG(error.localizedDescription)
                }
            } receiveValue: { [unowned self] response in
                self.isLoading = false
                let responseData = response.data
                
                do {
                    let jsonDecoder = JSONDecoder()
                    let decoded = try jsonDecoder.decode(ApiResponse<GetProfileResponse>.self, from: responseData)
                    
                    if let _ = decoded.data, decoded.success {
                        self.errorMessage = "비밀번호가 변경되었습니다."
                        self.isShowPopup = true
                        self.currentPassword = ""
                        self.newPassword = ""
                        self.newPasswordConfirm = ""
                        
                        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                            AppState.shared.back()
                        }
                    } else {
                        if let message = decoded.message {
                            self.errorMessage = message
                        } else {
                            self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        }
                        
                        self.isShowPopup = true
                    }
                } catch {
                    self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                    self.isShowPopup = true
                }
            }
            .store(in: &subscription)
    }
    
    func removeTag(tag: String) {
        if let index = tags.firstIndex(of: tag) {
            tags.remove(at: index)
        }
        
        if (insertTags.contains(tag)) {
            if let index = insertTags.firstIndex(of: tag) {
                insertTags.remove(at: index)
            }
        } else {
            removeTags.append(tag)
        }
    }
    
    func addTag(tag: String) {
        tags.append(tag)
        if (removeTags.contains(tag)) {
            if let index = removeTags.firstIndex(of: tag) {
                removeTags.remove(at: index)
            }
        } else {
            insertTags.append(tag)
        }
    }
    
    private func updateProfileImage() {
        isLoading = false
        
        if let profileImage = profileImage, let imageData = profileImage.jpegData(compressionQuality: 0.8) {
            repository
                .profileImageUpdate(parameter: MultipartFormData(
                    provider: .data(imageData),
                    name: "image",
                    fileName: "\(UUID().uuidString)_\(Date().timeIntervalSince1970 * 1000).jpg",
                    mimeType: "image/*"
                ))
                .sink { result in
                    switch result {
                    case .finished:
                        DEBUG_LOG("finish")
                    case .failure(let error):
                        ERROR_LOG(error.localizedDescription)
                    }
                } receiveValue: { [unowned self] response in
                    self.isLoading = false
                    let responseData = response.data
                    
                    do {
                        let jsonDecoder = JSONDecoder()
                        let decoded = try jsonDecoder.decode(ApiResponse<String>.self, from: responseData)
                        
                        if let profileImageUrl = decoded.data, decoded.success {
                            UserDefaults.set(profileImageUrl, forKey: .profileImage)
                            self.refresh()
                            self.getMyProfile()
                        } else {
                            if let message = decoded.message {
                                self.errorMessage = message
                            } else {
                                self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                            }
                            
                            self.isShowPopup = true
                        }
                    } catch {
                        self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        self.isShowPopup = true
                    }
                }
                .store(in: &subscription)
        } else {
            self.errorMessage = "프로필 이미지를 업데이트 하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
            self.isShowPopup = true
            self.isLoading = false
        }
    }
    
    private func validatePassword() -> Bool {
        let passwordRegEx = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d$@!%*#?&]{8,}$"
        
        let predicate = NSPredicate(format:"SELF MATCHES %@", passwordRegEx)
        return predicate.evaluate(with: newPassword)
    }
}
