//
//  AuditionRoleDetailViewModel.swift
//  SodaLive
//
//  Created by klaus on 1/6/25.
//

import Foundation
import Moya
import Combine

final class AuditionRoleDetailViewModel: ObservableObject {
    
    private let repository = AuditionRepository()
    private var subscription = Set<AnyCancellable>()
    
    @Published var errorMessage = ""
    @Published var isShowPopup = false
    @Published var isLoading = false
    
    @Published var totalCount = 0
    @Published var applicantList = [GetAuditionRoleApplicantItem]()
    
    @Published var name = "보이스온"
    @Published var auditionRoleDetail: GetAuditionRoleDetailResponse? = nil
    
    @Published private (set) var sortType = AuditionApplicantSortType.NEWEST {
        didSet {
            refreshApplicantList()
        }
    }
    
    @Published var fileName = ""
    @Published var soundData: Data? = nil
    @Published var phoneNumber = ""
    
    @Published var isShowNotifyVote = true
    @Published var isShowVoteCompleteView = false
    @Published var isShowNoticeReapply = false
    @Published var dialogTitle = ""
    @Published var dialogDesc = ""
    
    var page = 1
    var isLast = false
    private var pageSize = 10
    
    var auditionRoleId = -1 {
        didSet {
            if auditionRoleId > 0 {
                getAuditionRoleDetail()
            } else {
                onFailure()
            }
        }
    }
    
    var onFailure: () -> Void = {}
    
    func setSortType(sortType: AuditionApplicantSortType) {
        if self.sortType != sortType {
            self.sortType = sortType
        }
    }
    
    func getAuditionRoleDetail() {
        isLoading = true
        
        let auditionRoleDetail = repository.getAuditionRoleDetail(auditionRoleId: auditionRoleId)
        let auditionApplicantList = repository.getAuditionApplicantList(auditionRoleId: auditionRoleId, sortType: sortType, page: page, size: pageSize)
        
        Publishers
            .CombineLatest(auditionRoleDetail, auditionApplicantList)
            .sink { result in
                switch result {
                case .finished:
                    DEBUG_LOG("finish")
                case .failure(let error):
                    ERROR_LOG(error.localizedDescription)
                }
            } receiveValue: { [unowned self] (roleDetailResponse, applicantListResponse) in
                let roleDetail = roleDetailResponse.data
                let applicantList = applicantListResponse.data
                
                let jsonDecoder = JSONDecoder()
                
                do {
                    let roleDetailDecoded = try jsonDecoder.decode(ApiResponse<GetAuditionRoleDetailResponse>.self, from: roleDetail)
                    if let data = roleDetailDecoded.data, roleDetailDecoded.success {
                        self.name = data.name
                        self.isShowNoticeReapply = data.isAlreadyApplicant
                        self.auditionRoleDetail = data
                    } else {
                        if let message = roleDetailDecoded.message {
                            self.errorMessage = message
                        } else {
                            self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        }
                        
                        self.isShowPopup = true
                    }
                } catch {
                    self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                    self.isShowPopup = true
                }
                
                do {
                    let applicantListDecoded = try jsonDecoder.decode(ApiResponse<GetAuditionApplicantListResponse>.self, from: applicantList)
                    if let data = applicantListDecoded.data, applicantListDecoded.success {
                        self.totalCount = data.totalCount
                        self.applicantList.append(contentsOf: data.items)
                        
                        if data.items.isEmpty {
                            isLast = true
                        } else {
                            page += 1
                        }
                    } else {
                        if let message = applicantListDecoded.message {
                            self.errorMessage = message
                        } else {
                            self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        }
                        
                        self.isShowPopup = true
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                            self.onFailure()
                        }
                    }
                } catch {
                    self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                    self.isShowPopup = true
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        self.onFailure()
                    }
                }
                
                self.isLoading = false
            }
            .store(in: &subscription)
    }
    
    func getAuditionApplicantList() {
        if !isLoading && !isLast {
            isLoading = true
            
            repository.getAuditionApplicantList(auditionRoleId: auditionRoleId, sortType: sortType, page: page, size: pageSize)
                .sink { result in
                    switch result {
                    case .finished:
                        DEBUG_LOG("finish")
                    case .failure(let error):
                        ERROR_LOG(error.localizedDescription)
                    }
                } receiveValue: { [unowned self] response in
                    let responseData = response.data
                    self.isLoading = false
                    
                    do {
                        let jsonDecoder = JSONDecoder()
                        let decoded = try jsonDecoder.decode(ApiResponse<GetAuditionApplicantListResponse>.self, from: responseData)
                        
                        if let data = decoded.data, decoded.success {
                            self.totalCount = data.totalCount
                            self.applicantList.append(contentsOf: data.items)
                            
                            if data.items.isEmpty {
                                isLast = true
                            } else {
                                page += 1
                            }
                        } 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 applyAudition(onSuccess: @escaping () -> Void) {
        if phoneNumber.count != 11 {
            errorMessage = "잘못된 연락처 입니다.\n다시 입력해 주세요."
            isShowPopup = true
            return
        }
        
        guard let soundData = soundData else {
            errorMessage = "잘못된 녹음 파일 입니다.\n다시 선택해 주세요."
            isShowPopup = true
            return
        }
        
        isLoading = true
        
        let request = ApplyAuditionRoleRequest(roleId: auditionRoleId, phoneNumber: phoneNumber)
        var multipartData = [MultipartFormData]()
        
        let encoder = JSONEncoder()
        encoder.outputFormatting = .withoutEscapingSlashes
        let jsonData = try? encoder.encode(request)
        
        if let jsonData = jsonData {
            multipartData.append(MultipartFormData(provider: .data(jsonData), name: "request"))
            multipartData.append(
                MultipartFormData(
                    provider: .data(soundData),
                    name: "contentFile",
                    fileName: fileName,
                    mimeType: "audio/*"
                )
            )
            
            repository.applyAudition(parameters: multipartData)
                .sink { result in
                    switch result {
                    case .finished:
                        DEBUG_LOG("finish")
                    case .failure(let error):
                        ERROR_LOG(error.localizedDescription)
                    }
                } receiveValue: { response in
                    self.isLoading = false
                    let responseData = response.data
                    
                    do {
                        let jsonDecoder = JSONDecoder()
                        let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
                        
                        if decoded.success {
                            self.deleteAllRecordingFilesWithNamePrefix("voiceon_now_voice")
                            
                            self.phoneNumber = ""
                            self.fileName = ""
                            self.soundData = nil
                            
                            self.refreshApplicantList()
                            onSuccess()
                        } else {
                            if let message = decoded.message {
                                self.errorMessage = message
                            } else {
                                self.errorMessage = "오디션 지원을 완료하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                            }
                            
                            self.isShowPopup = true
                        }
                    } catch {
                        self.errorMessage = "오디션 지원을 완료하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
                        self.isShowPopup = true
                    }
                }
                .store(in: &subscription)
        } else {
            self.errorMessage = "오디션 지원을 완료하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
            self.isShowPopup = true
            self.isLoading = false
        }
    }
    
    func voteApplicant(applicantId: Int) {
        isLoading = true
        
        repository.voteApplicant(applicantId: applicantId)
            .sink { result in
                switch result {
                case .finished:
                    DEBUG_LOG("finish")
                case .failure(let error):
                    ERROR_LOG(error.localizedDescription)
                }
            } receiveValue: { response in
                self.isLoading = false
                let responseData = response.data
                
                do {
                    let jsonDecoder = JSONDecoder()
                    let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
                    
                    if decoded.success {
                        if self.isShowNotifyVote {
                            self.dialogTitle = "[오디션 응원]"
                            self.dialogDesc = "오디션을 응원하셨습니다\n(무료응원 : 1계정당 1일 1회)\n1캔으로 추가 응원을 해보세요."
                            self.isShowVoteCompleteView = true
                        }
                        
                        if let index = self.applicantList.firstIndex(where: { $0.applicantId == applicantId }) {
                            var applicant = self.applicantList[index]
                            applicant.voteCount += 1
                            
                            self.applicantList.remove(at: index)
                            self.applicantList.insert(applicant, at: index)
                        }
                    } else {
                        if let message = decoded.message {
                            if message.contains("오늘 응원은 여기까지") {
                                self.dialogTitle = "[오늘 응원 제한]"
                                self.dialogDesc = "오늘 응원은 여기까지!\n하루 최대 10회까지 이용이 가능합니다.\n내일 다시 이용해주세요."
                                self.isShowVoteCompleteView = true
                            } else {
                                self.errorMessage = message
                                self.isShowPopup = true
                            }
                        } else {
                            self.errorMessage = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                            self.isShowPopup = true
                        }
                    }
                } catch {
                    self.errorMessage = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
                    self.isShowPopup = true
                }
            }
            .store(in: &subscription)
    }
    
    private func refreshApplicantList() {
        self.page = 1
        self.isLast = false
        self.totalCount = 0
        self.applicantList = []
        self.getAuditionApplicantList()
    }
    
    func deleteAllRecordingFilesWithNamePrefix(_ prefix: String) {
        let fileManager = FileManager.default
        let documentsURL = getDocumentsDirectory()

        do {
            let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil, options: [])
            for fileURL in fileURLs {
                if fileURL.lastPathComponent.hasPrefix(prefix) {
                    try fileManager.removeItem(at: fileURL)
                    DEBUG_LOG("녹음 파일 삭제 성공: \(fileURL)")
                }
            }
        } catch {
            DEBUG_LOG("녹음 파일 삭제 실패: \(error.localizedDescription)")
        }
    }
    
    private func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
}