//
//  LNEditVoicePanel.swift
//  Gami
//
//  Created by OneeChan on 2026/1/14.
//

import Foundation
import UIKit
import SnapKit


enum LNVoiceEditState {
    case record
    case edit
    case review
    case play
}


class LNEditVoicePanel: LNPopupView {
    private let minDuration: Double = 3
    private let maxDuration: Double = 60
    private let recordView = UIView()
    private let recordDurationLabel = UILabel()
    private let recordButton = UIButton()
    private let recordText = UILabel()
    private var recordTaskId: String?
    
    private let editView = UIView()
    private let editPlayButton = UIButton()
    private let editDurationLabel = UILabel()
    private var curUrl: URL?
    private var curDuration: Double?
    
    private let reviewView = UIView()
    
    private let displayView = UIView()
    private let playIcon = UIImageView()
    private let voiceWaveView = LNVoiceWaveView()
    private let playDurationLabel = UILabel()
    
    private var curState: LNVoiceEditState = .record {
        didSet {
            recordView.isHidden = curState != .record
            editView.isHidden = curState != .edit
            reviewView.isHidden = curState != .review
            displayView.isHidden = curState != .play
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupViews()
        adjustByUserInfo()
        
        onTouchOutside = { [weak self] in
            guard let self else { return }
            LNVoicePlayer.shared.stop()
            if LNVoiceRecorder.shared.isRecording {
                let (url, duration) = LNVoiceRecorder.shared.stopRecord()
                handleRecordResult(url: url, duration: duration)
            } else if curState == .edit {
                let alert = LNCommonAlertView()
                alert.titleLabel.text = .init(key: "B00018")
                alert.showConfirm { [weak self] in
                    guard let self else { return }
                    dismiss()
                }
                alert.showCancel()
                alert.popup()
            } else {
                dismiss()
            }
        }
        
        LNEventDeliver.addObserver(self)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension LNEditVoicePanel {
    private func adjustByUserInfo() {
        if myVoiceBarInfo.status == .review {
            curState = .review
        } else if myVoiceBarInfo.status == .done,
                  !myUserInfo.voiceBar.isEmpty {
            curState = .play
            playDurationLabel.text = Double(myVoiceBarInfo.voiceBarDuration).durationDisplay
        } else {
            curState = .record
        }
    }
    
    private func handleRecordResult(url: URL?, duration: Double) {
        guard let url else {
            return
        }
        if duration < minDuration {
            showToast(.init(key: "B00009", minDuration))
            return
        }
        curUrl = url
        curDuration = duration
        editDurationLabel.text = duration.timeCountDisplay
        curState = .edit
    }
}

extension LNEditVoicePanel: LNVoicePlayerNotify {
    func onAudioUpdateDuration(path: String, cur: TimeInterval, total: TimeInterval) {
        if !editView.isHidden {
            guard curUrl?.path == path else { return }
            let remain = Int(total - cur)
            editDurationLabel.text = remain.timeCountDisplay
        } else if !displayView.isHidden {
            guard path == myUserInfo.voiceBar else { return }
            playDurationLabel.text = (total - cur).durationDisplay
        }
    }
    
    func onAudioStopPlay(path: String) {
        if !editView.isHidden {
            guard curUrl?.path == path else { return }
            guard let curDuration else { return }
            editDurationLabel.text = curDuration.timeCountDisplay
            editPlayButton.setImage(.icVoiceEditPlay, for: .normal)
        } else if !displayView.isHidden {
            guard path == myUserInfo.voiceBar else { return }
            playDurationLabel.text = "\(myVoiceBarInfo.voiceBarDuration)“"
            playIcon.image = .icVoicePlay
            voiceWaveView.stopAnimate()
        }
    }
    
    func onAudioStartPlay(path: String) {
        if !editView.isHidden {
            guard curUrl?.path == path else { return }
            editPlayButton.setImage(.icVoiceEditPause, for: .normal)
        } else if !displayView.isHidden {
            guard path == myUserInfo.voiceBar else { return }
            playIcon.image = .icVoicePause
            voiceWaveView.startAnimate()
        }
    }
}

extension LNEditVoicePanel: LNVoiceRecorderNotify {
    func onRecordTaskDurationChanged(taskId: String, duration: Double, volumeRatio: Double) {
        guard recordTaskId == taskId else { return }
        
        recordDurationLabel.text = duration.timeCountDisplay
    }
    
    func onRecordTaskRecording(taskId: String) {
        guard recordTaskId == taskId else { return }
        
        recordText.text = .init(key: "B00008")
        recordButton.setImage(.icVoiceEditStop, for: .normal)
    }
    
    func onRecordTaskStop(taskId: String) {
        guard recordTaskId == taskId else { return }
        
        resetRecord()
    }
    
    func onRecordTaskReachMaxDuration(taskId: String, fileUrl: URL?, duration: Double) {
        guard recordTaskId == taskId else { return }
        
        handleRecordResult(url: fileUrl, duration: duration)
    }
}

extension LNEditVoicePanel {
    private func resetRecord() {
        recordDurationLabel.text = "00:00"
        recordButton.setImage(.icVoiceEditStart, for: .normal)
        recordText.text = .init(key: "B00007")
    }
    
    private func setupViews() {
        let fakeView = UIView()
        fakeView.isUserInteractionEnabled = false
        container.addSubview(fakeView)
        fakeView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.height.equalTo(326)
        }
        
        let titleView = buildTitle()
        container.addSubview(titleView)
        titleView.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.top.equalToSuperview()
        }
        
        let recordView = buildRecordView()
        container.addSubview(recordView)
        recordView.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.top.equalTo(titleView.snp.bottom)
            make.bottom.equalToSuperview()
        }
        
        let editView = buildEditView()
        container.addSubview(editView)
        editView.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.top.equalTo(titleView.snp.bottom)
            make.bottom.equalToSuperview()
        }
        
        let reviewView = buildReviewView()
        container.addSubview(reviewView)
        reviewView.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.top.equalTo(titleView.snp.bottom)
            make.bottom.equalToSuperview()
        }
        
        let displayView = buildDisplayView()
        container.addSubview(displayView)
        displayView.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.top.equalTo(titleView.snp.bottom)
            make.bottom.equalToSuperview()
        }
    }
    
    private func buildTitle() -> UIView {
        let container = UIView()
        container.snp.makeConstraints { make in
            make.height.equalTo(50)
        }
        
        let titleLabel = UILabel()
        titleLabel.text = .init(key: "B00006")
        container.addSubview(titleLabel)
        titleLabel.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        
        return container
    }
    
    private func buildRecordView() -> UIView {
        recordDurationLabel.font = .body_m
        recordDurationLabel.textColor = .text_5
        recordView.addSubview(recordDurationLabel)
        recordDurationLabel.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalToSuperview().offset(21)
        }
        
        recordView.addSubview(recordButton)
        recordButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            if LNVoiceRecorder.shared.isRecording {
                let (url, duration) = LNVoiceRecorder.shared.stopRecord()
                handleRecordResult(url: url, duration: duration)
            } else {
                LNVoiceRecorder.shared.startRecord(maxDuration) { [weak self] taskId in
                    guard let self else { return }
                    recordTaskId = taskId
                }
            }
        }), for: .touchUpInside)
        recordButton.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(recordDurationLabel.snp.bottom).offset(12)
        }
        
        recordText.font = .body_m
        recordText.textColor = .text_4
        recordView.addSubview(recordText)
        recordText.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(recordButton.snp.bottom).offset(12)
        }
        
        let confirmButton = UIButton()
        confirmButton.setTitle(.init(key: "A00240"), for: .normal)
        confirmButton.setTitleColor(.text_1, for: .normal)
        confirmButton.titleLabel?.font = .heading_h3
        confirmButton.layer.cornerRadius = 23.5
        confirmButton.backgroundColor = .fill_4
        confirmButton.isEnabled = false
        recordView.addSubview(confirmButton)
        confirmButton.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview().inset(12)
            make.bottom.equalToSuperview().offset(commonBottomInset)
            make.height.equalTo(47)
        }
        
        resetRecord()
        
        return recordView
    }
    
    private func buildEditView() -> UIView {
        editView.isHidden = true
        
        editDurationLabel.font = .body_m
        editDurationLabel.textColor = .text_5
        editView.addSubview(editDurationLabel)
        editDurationLabel.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalToSuperview()
        }
        
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.spacing = 24
        editView.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(editDurationLabel.snp.bottom).offset(34)
        }
        
        let remakeView = UIView()
        stackView.addArrangedSubview(remakeView)
        
        let remakeButton = UIButton()
        remakeButton.setImage(.icVoiceEditRemake, for: .normal)
        remakeButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            LNVoicePlayer.shared.stop()
            curUrl = nil
            curDuration = nil
            curState = .record
        }), for: .touchUpInside)
        remakeView.addSubview(remakeButton)
        remakeButton.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.leading.greaterThanOrEqualToSuperview()
            make.top.equalToSuperview()
        }
        
        let remakeLabel = UILabel()
        remakeLabel.text = .init(key: "B00010")
        remakeLabel.font = .body_m
        remakeLabel.textColor = .text_4
        remakeLabel.textAlignment = .center
        remakeView.addSubview(remakeLabel)
        remakeLabel.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.bottom.equalToSuperview()
            make.top.equalTo(remakeButton.snp.bottom).offset(12)
        }
        
        let playView = UIView()
        stackView.addArrangedSubview(playView)
        
        editPlayButton.setImage(.icVoiceEditPlay, for: .normal)
        editPlayButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            guard let curUrl else { return }
            if LNVoicePlayer.shared.isPlaying {
                LNVoicePlayer.shared.stop()
            } else {
                LNVoicePlayer.shared.play(path: curUrl.path)
            }
        }), for: .touchUpInside)
        playView.addSubview(editPlayButton)
        editPlayButton.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.leading.greaterThanOrEqualToSuperview()
            make.top.equalToSuperview()
        }
        
        let playLabel = UILabel()
        playLabel.text = .init(key: "B00011")
        playLabel.font = .body_m
        playLabel.textColor = .text_4
        playLabel.textAlignment = .center
        playView.addSubview(playLabel)
        playLabel.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.bottom.equalToSuperview()
            make.top.equalTo(editPlayButton.snp.bottom).offset(12)
        }
        
        let confirmButton = UIButton()
        confirmButton.setTitle(.init(key: "A00240"), for: .normal)
        confirmButton.setTitleColor(.text_1, for: .normal)
        confirmButton.titleLabel?.font = .heading_h3
        confirmButton.layer.cornerRadius = 23.5
        confirmButton.setBackgroundImage(.primary_8, for: .normal)
        confirmButton.clipsToBounds = true
        confirmButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            guard let curUrl, let curDuration else { return }
            LNVoicePlayer.shared.stop()
            showLoading()
            LNFileUploader.shared.startUpload(type: .voice, fileURL: curUrl,
                                              progressHandler: nil) { [weak self] url, err in
                dismissLoading()
                guard let self else { return }
                guard let url, err == nil else {
                    showToast(err)
                    return
                }
                LNProfileManager.shared.setMyVoiceBar(url: url, duration: curDuration.toDuration)
                { [weak self] success in
                    guard let self else { return }
                    guard success else { return }
                    dismiss()
                }
            }
        }), for: .touchUpInside)
        editView.addSubview(confirmButton)
        confirmButton.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview().inset(12)
            make.bottom.equalToSuperview().offset(commonBottomInset)
            make.height.equalTo(47)
        }
        
        return editView
    }
    
    private func buildReviewView() -> UIView {
        let icon = UIImageView()
        icon.image = .icVoiceEditReview
        reviewView.addSubview(icon)
        icon.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalToSuperview().offset(47)
        }
        
        let titleLabel = UILabel()
        titleLabel.text = .init(key: "B00012")
        titleLabel.textColor = .text_5
        titleLabel.font = .heading_h4
        reviewView.addSubview(titleLabel)
        titleLabel.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(icon.snp.bottom).offset(12)
        }
        
        let descLabel = UILabel()
        descLabel.text = .init(key: "B00013")
        descLabel.font = .body_m
        descLabel.textColor = .text_5
        reviewView.addSubview(descLabel)
        descLabel.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(titleLabel.snp.bottom).offset(3)
        }
        
        return reviewView
    }
    
    private func buildDisplayView() -> UIView {
        let button = UIButton()
        button.setBackgroundImage(.primary_7, for: .normal)
        button.layer.cornerRadius = 16
        button.clipsToBounds = true
        displayView.addSubview(button)
        button.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalToSuperview().offset(59)
            make.width.equalTo(163)
            make.height.equalTo(32)
        }
        
        playIcon.image = .icVoicePlay
        button.addSubview(playIcon)
        playIcon.snp.makeConstraints { make in
            make.centerY.equalToSuperview()
            make.leading.equalToSuperview().offset(3)
            make.width.height.equalTo(22)
        }
        
        voiceWaveView.isUserInteractionEnabled = false
        voiceWaveView.build()
        button.addSubview(voiceWaveView)
        voiceWaveView.snp.makeConstraints { make in
            make.centerY.equalToSuperview()
            make.leading.equalTo(playIcon.snp.trailing).offset(7)
            make.width.equalTo(19)
            make.height.equalTo(11)
        }
        
        playDurationLabel.font = .heading_h5
        playDurationLabel.textColor = .text_1
        button.addSubview(playDurationLabel)
        playDurationLabel.snp.makeConstraints { make in
            make.centerY.equalToSuperview()
            make.trailing.equalToSuperview().offset(-11)
        }
        
        button.addAction(UIAction(handler: { [weak self] _ in
            guard self != nil else { return }
            if LNVoicePlayer.shared.isPlaying {
                LNVoicePlayer.shared.stop()
            } else if !myUserInfo.voiceBar.isEmpty {
                LNVoicePlayer.shared.play(myUserInfo.voiceBar)
            }
        }), for: .touchUpInside)
        
        let confirmButton = UIButton()
        confirmButton.setTitle(.init(key: "B00107"), for: .normal)
        confirmButton.setTitleColor(.text_6, for: .normal)
        confirmButton.titleLabel?.font = .heading_h3
        confirmButton.layer.cornerRadius = 23.5
        confirmButton.setBackgroundImage(.primary_7, for: .normal)
        confirmButton.clipsToBounds = true
        confirmButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            LNVoicePlayer.shared.stop()
            
            LNProfileManager.shared.cleanVoiceBar { [weak self] success in
                guard let self else { return }
                guard success else { return }
                resetRecord()
                curState = .record
            }
        }), for: .touchUpInside)
        displayView.addSubview(confirmButton)
        confirmButton.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview().inset(12)
            make.bottom.equalToSuperview().offset(commonBottomInset)
            make.height.equalTo(47)
        }
        
        let cover = UIView()
        cover.layer.cornerRadius = 22
        cover.backgroundColor = .fill
        cover.isUserInteractionEnabled = false
        confirmButton.insertSubview(cover, at: 0)
        cover.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(1)
        }
        
        return displayView
    }
}
